From 8ba384cc322a6a74112d80dc49c3cac40ee102e2 Mon Sep 17 00:00:00 2001 From: yujun Date: Tue, 30 Jun 2026 11:13:24 +0800 Subject: [PATCH 001/860] migration: Fix invalid %ud format and trace arg typo Standard printf has no %ud conversion; glibc treats it as %u followed by a literal 'd', so postcopy userfaultfd error messages printed event values like "5d" instead of "5". The same typo existed in two postcopy discard trace format strings. Also rename the misspelled ram_save_iterate_big_wait() trace argument milliconds to milliseconds. Fixes: 00fa4fc85b ("postcopy: Allow registering of fd handler") Signed-off-by: yujun Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20260630031324.43453-1-yujun@kylinos.cn Signed-off-by: Peter Xu --- migration/postcopy-ram.c | 4 ++-- migration/trace-events | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index f5ef93f193..980b938a4c 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -1368,7 +1368,7 @@ static void *postcopy_ram_fault_thread(void *opaque) } } if (msg.event != UFFD_EVENT_PAGEFAULT) { - error_report("%s: Read unexpected event %ud from userfaultfd", + error_report("%s: Read unexpected event %u from userfaultfd", __func__, msg.event); continue; /* It's not a page fault, shouldn't happen */ } @@ -1443,7 +1443,7 @@ retry: } } if (msg.event != UFFD_EVENT_PAGEFAULT) { - error_report("%s: Read unexpected event %ud " + error_report("%s: Read unexpected event %u " "from userfaultfd (shared)", __func__, msg.event); continue; /* It's not a page fault, shouldn't happen */ diff --git a/migration/trace-events b/migration/trace-events index de99d976ab..3b1258c3bf 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -21,14 +21,14 @@ loadvm_postcopy_handle_run(void) "" loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" -loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud" +loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %u" loadvm_process_command(const char *s, uint16_t len) "com=%s len=%d" loadvm_process_command_ping(uint32_t val) "0x%x" loadvm_approve_switchover(unsigned int switchover_ack_pending_num) "Switchover ack pending num=%u" postcopy_ram_listen_thread_exit(void) "" postcopy_ram_listen_thread_start(void) "" qemu_savevm_send_postcopy_advise(void) "" -qemu_savevm_send_postcopy_ram_discard(const char *id, uint16_t len) "%s: %ud" +qemu_savevm_send_postcopy_ram_discard(const char *id, uint16_t len) "%s: %u" savevm_command_send(uint16_t command, uint16_t len) "com=0x%x len=%d" savevm_section_start(const char *id, unsigned int section_id) "%s, section_id %u" savevm_section_end(const char *id, unsigned int section_id, int ret) "%s, section_id %u -> %d" @@ -119,7 +119,7 @@ colo_flush_ram_cache_begin(uint64_t dirty_pages) "dirty_pages %" PRIu64 colo_flush_ram_cache_end(void) "" save_xbzrle_page_skipping(void) "" save_xbzrle_page_overflow(void) "" -ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" +ram_save_iterate_big_wait(uint64_t milliseconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" ram_load_start(void) "" ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu" From 8b61adae4e25d9f6ec17462ac2b6f7ad6b5bab14 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 1 Jul 2026 08:11:36 +0200 Subject: [PATCH 002/860] qapi: Fix misspelled section tags in doc comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Section tags are case sensitive and end with a colon. Screwing up either gets them interpreted as ordinary paragraph. Fix a few. Fixes: 4e88e7e340 (qapi/qom: Define cache enumeration and properties for machine, 2024-11-01) Fixes: 8eb6d39e22 (qom: qom-list-get, 2025-07-11) Signed-off-by: Markus Armbruster Message-ID: <20260701061136.798815-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- qapi/machine-common.json | 2 +- qapi/qom.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qapi/machine-common.json b/qapi/machine-common.json index 92e84dfb14..aca98e994a 100644 --- a/qapi/machine-common.json +++ b/qapi/machine-common.json @@ -108,7 +108,7 @@ # # @caches: the list of `SmpCacheProperties`. # -# Since 9.2 +# Since: 9.2 ## { 'struct': 'SmpCachePropertiesWrapper', 'data': { 'caches': ['SmpCacheProperties'] } } diff --git a/qapi/qom.json b/qapi/qom.json index d96cc5aa21..c55776af7d 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -58,7 +58,7 @@ # @value: the value of the property. Absent when the property cannot # be read. # -# Since 10.1 +# Since: 10.1 ## { 'struct': 'ObjectPropertyValue', 'data': { 'name': 'str', @@ -70,7 +70,7 @@ # # @properties: a list of properties. # -# Since 10.1 +# Since: 10.1 ## { 'struct': 'ObjectPropertiesValues', 'data': { 'properties': [ 'ObjectPropertyValue' ] }} @@ -167,7 +167,7 @@ # Returns: A list where each element is the result for the # corresponding element of @paths. # -# Since 10.1 +# Since: 10.1 ## { 'command': 'qom-list-get', 'data': { 'paths': [ 'str' ] }, From 9dbce799e7087a75851ba1ae321b22c19b6f75c3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 8 Jul 2026 16:09:48 +0200 Subject: [PATCH 003/860] qom: Fix device-list-properties & friends to show legacy-FOO props MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qmp_device_list_properties() skips properties whose name starts with "legacy-". This is a flawed test for "is a legacy property". The test is flawed because non-legacy properties can and do start with "legacy-". Back when it was added, no such properties existed. Right now, three such properties do: property "legacy-cmb" of device "nvme", and properties "legacy-cache" and "legacy-multi-node" of devices "x86_64-cpu", "i386-cpu", and its children. This affects QMP command "device-list-properties", HMP command "device_add T,help", and command line option "-device T,help". Legacy properties are gone since commit a61383f7ab (qdev: Legacy properties are now unused internally, drop, 2025-10-22). This makes the fix easy: delete the code that skips them. Reproducer: -device nvme,help doesn't show legacy-cmb before the patch, and does after. Fixes: f4eb32b590 (qmp: show QOM properties in device-list-properties, 2014-05-20) Signed-off-by: Markus Armbruster Message-ID: <20260708140948.2622814-1-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- qom/qom-qmp-cmds.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 48b38d2b7f..330895361d 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -218,13 +218,6 @@ ObjectPropertyInfoList *qmp_device_list_properties(const char *typename, continue; } - /* Skip legacy properties since they are just string versions of - * properties that we already list. - */ - if (strstart(prop->name, "legacy-", NULL)) { - continue; - } - info = g_new0(ObjectPropertyInfo, 1); info->name = g_strdup(prop->name); info->type = g_strdup(prop->type); From f87693a841337badb894c28f2089a104a13557d5 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 10 Jul 2026 13:14:03 +0200 Subject: [PATCH 004/860] MAINTAINERS: Regularise the status fields (again) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Orphaned isn't a state, Orphan is. Fixes: fb7001e458 (MAINTAINERS: Remove PhilMD from firmware sections, 2026-04-17) Signed-off-by: Markus Armbruster Message-ID: <20260710111403.2953873-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index e25df9493c..8f84f3c61b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2937,7 +2937,7 @@ F: include/hw/isa/vt82c686.h Firmware configuration (fw_cfg) R: Gerd Hoffmann -S: Orphaned +S: Orphan F: docs/specs/fw_cfg.rst F: hw/nvram/fw_cfg*.c F: stubs/fw_cfg.c From 87f608e3c20677e57fc1c854085218dab8a45946 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 13 Jul 2026 13:46:22 +0200 Subject: [PATCH 005/860] json-parser: fix formatting of comment Signed-off-by: Paolo Bonzini Message-ID: <20260713114622.1950506-1-pbonzini@redhat.com> Reviewed-by: Markus Armbruster [Mea culpa] Signed-off-by: Markus Armbruster --- qobject/json-parser.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 5935730c0c..6b1b4762bd 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -29,9 +29,10 @@ * object, an error, or NULL (if the object is incomplete and no error * happened) after every token. Therefore it has an explicit * representation of its parser stack; each stack entry consists of a - * parser state and a QObject: - a QList, for an array that is being - * added to - a QDict, for a dictionary that is being added to - a - * QString, for the key of the next pair that will be added to a QDict + * parser state and a QObject: + * - a QList, for an array that is being added to + * - a QDict, for a dictionary that is being added to + * - a QString, for the key of the next pair that will be added to a QDict * * The stack represents an arbitrary nesting of arrays and dictionaries * (whose next key has been parsed); it can also have a dictionary whose From 506fa577db1df370eb4894f34a6d9ee28aa1d679 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 9 Mar 2026 19:14:52 +0100 Subject: [PATCH 006/860] hw/sparc64/sun4u: Mark unusable PCI busses as full to ease device plugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When trying to plug a PCI device to a Sparc64 machine, you currently have to specify the right bus ("bus=pciB"), otherwise you get this error: $ qemu-system-sparc64 -device virtio-scsi-pci qemu-system-sparc64: -device virtio-scsi-pci: PCI: no slot/function available for virtio-scsi-pci, all in use or reserved This is quite annoying for the unexperienced users, and it also breaks e.g. the iotests ("make check-block") when running with qemu-system-sparc64. Mark the non-usable PCI busses as full now, so that QEMU can automatically plug new PCI devices to the right "pciB" bus. Signed-off-by: Thomas Huth Message-ID: <20260309181452.83702-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sparc64/sun4u.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d69ed9a81a..a348096aee 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -719,6 +719,13 @@ static void sun4uv_init(MemoryRegion *address_space_mem, fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_DEPTH, graphic_depth); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); + + /* + * Mark internal PCI busses as full so that the plugging of additional + * PCI devices happens on the right bus that still has free slots: + */ + qbus_mark_full(&pci_bus->qbus); + qbus_mark_full(&pci_busA->qbus); } enum { From 5157ceb10ca83d1c257ff84368458153a10a2f23 Mon Sep 17 00:00:00 2001 From: Haotian Jiang Date: Mon, 29 Jun 2026 10:59:03 +0800 Subject: [PATCH 007/860] hw/misc/ivshmem: clear chardev handlers before freeing peers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ivshmem_exit() frees s->peers and s->msi_vectors but does not clear the chardev handlers registered in ivshmem_common_realize(). Those handlers are only removed later in object_finalize() via release_chr, which runs after ivshmem_exit(). Between exit and finalize, ivshmem_read() can fire on pending chardev data and process_msg_connect() dereferences the freed s->peers. Additionally, s->peers, s->nb_peers, and s->msi_vectors are not zeroed after free, leaving dangling pointers that make the UAF code paths reachable. Fix by clearing chardev handlers at the beginning of ivshmem_exit(), before any resources they access are freed, and nullifying freed pointers. Cc: qemu-stable@nongnu.org Fixes: f64a078d45a ("ivshmem: fix pci_ivshmem_exit()") Link: https://gitlab.com/qemu-project/qemu/-/work_items/3594 Reported-by: Haotian Jiang Signed-off-by: Haotian Jiang Message-ID: Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/ivshmem-pci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index 536475e9de..372e9dc58d 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -938,6 +938,9 @@ static void ivshmem_exit(PCIDevice *dev) IVShmemState *s = IVSHMEM_COMMON(dev); int i; + qemu_chr_fe_set_handlers(&s->server_chr, + NULL, NULL, NULL, NULL, NULL, NULL, true); + migrate_del_blocker(&s->migration_blocker); if (memory_region_is_mapped(s->ivshmem_bar2)) { @@ -966,6 +969,8 @@ static void ivshmem_exit(PCIDevice *dev) close_peer_eventfds(s, i); } g_free(s->peers); + s->peers = NULL; + s->nb_peers = 0; } if (ivshmem_has_feature(s, IVSHMEM_MSI)) { @@ -973,6 +978,7 @@ static void ivshmem_exit(PCIDevice *dev) } g_free(s->msi_vectors); + s->msi_vectors = NULL; } static int ivshmem_pre_load(void *opaque) From 6387fbad45eb89c160e1d766a4e170e896bc3fff Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 30 Jun 2026 21:57:29 +0800 Subject: [PATCH 008/860] docs/devel: Document SSI dummy-cycle ownership MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the boundary between SPI/SSI controller models and SPI flash models when representing fast-read dummy cycles. It explains that flash models own command semantics, while controllers own hardware-generated dummy transfers and cycle-to-byte conversion. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260707083431.219671-11-bin.meng@processmission.com> [PMD: Update MAINTAINERS] Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + docs/devel/index-internals.rst | 1 + docs/devel/ssi.rst | 132 +++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 docs/devel/ssi.rst diff --git a/MAINTAINERS b/MAINTAINERS index ecb8cfdc41..f06a278806 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2350,6 +2350,7 @@ T: git https://github.com/bonzini/qemu.git scsi-next SSI M: Alistair Francis S: Maintained +F: docs/devel/ssi.rst F: hw/ssi/* F: hw/block/m25p80* F: include/hw/ssi/ssi.h diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index b89bab9b30..a8f5e310df 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -20,6 +20,7 @@ Details about QEMU's various subsystems including how to add features to them. reset s390-cpu-topology s390-dasd-ipl + ssi tracing uefi-vars vfio-iommufd diff --git a/docs/devel/ssi.rst b/docs/devel/ssi.rst new file mode 100644 index 0000000000..864b5d9320 --- /dev/null +++ b/docs/devel/ssi.rst @@ -0,0 +1,132 @@ +================================ +SSI devices and SPI flash models +================================ + +QEMU's Synchronous Serial Interface (SSI) bus models the full-duplex transfer +of words between a controller and one selected peripheral. Most SPI flash +models, including ``m25p80``, are attached to controllers through this bus. + +This page documents the expected boundary between a controller model and a +flash model for SPI fast-read dummy cycles. The boundary is important because +many real controllers expose dummy-cycle configuration in registers, while the +flash model observes only the byte stream delivered through ``ssi_transfer()``. + +SSI transfer granularity +------------------------ + +``ssi_transfer()`` transfers one SSI word. Flash models that implement common +SPI NOR command streams usually consume one 8-bit word at a time: + +* command opcode; +* address bytes; +* optional mode or continuous-read bytes; +* dummy bytes; +* data bytes. + +The SSI core does not model individual clock edges or the number of active SPI +data lines. If a real transaction has a dummy phase expressed in clock cycles, +the device model that generates transfers on the SSI bus must represent that +phase as a number of dummy byte transfers. + +Flash model responsibilities +---------------------------- + +A SPI flash model owns the command semantics for the flash device: + +* which opcodes are recognized; +* how many address bytes are required; +* whether a command has mode bytes; +* how many dummy bytes must be consumed before data can be returned; +* manufacturer-specific differences in fast-read command behavior. + +For the ``m25p80`` model, ``needed_bytes`` is a byte count. It must not store +raw dummy cycles. When a flash datasheet describes the dummy phase in cycles, +the flash model converts the cycles to bytes using the bus width used for the +dummy phase:: + + dummy_bytes = DIV_ROUND_UP(dummy_cycles * dummy_bus_width, 8) + +For SPI NOR fast-read commands modeled by ``m25p80``, the dummy phase follows +the address phase width. For example, output-only dual and quad read commands +such as DOR and QOR use one line for command, address, and dummy phases, then +use two or four lines only for the data phase. Dual I/O and Quad I/O commands +such as DIOR and QIOR use the wider bus for both address and dummy phases. + +If the exact dummy phase cannot be represented as a whole number of SSI byte +transfers, the model should round up and log the limitation instead of silently +treating cycles as bytes. + +Controller model responsibilities +--------------------------------- + +A controller model owns the behavior of the controller hardware: + +* how guest-visible registers select command, address width, bus width, and + dummy-cycle count; +* whether the guest supplies dummy bytes in a transmit FIFO; +* whether the controller itself generates the dummy phase for a memory-mapped, + direct-read, or other automatic transfer mode; +* how chip-select state changes around controller-generated transfers. + +When guest software writes dummy bytes into a transmit FIFO or manual transfer +path, the controller should pass those bytes to ``ssi_transfer()`` like any +other guest-provided byte. It should not add more dummy transfers on behalf of +the flash. + +When hardware registers instruct the controller to generate a dummy phase, the +controller must emit dummy byte transfers before data transfers reach the flash +model. The controller should convert the configured cycle count using the bus +width that the controller uses during the dummy phase. For example: + +* 8 dummy cycles on a single data line become 1 dummy byte; +* 8 dummy cycles on two data lines become 2 dummy bytes; +* 8 dummy cycles on four data lines become 4 dummy bytes. + +The controller should not duplicate flash-specific opcode tables merely to +guess which commands need dummy cycles. In automatic modes the controller +already has enough hardware configuration to know whether it must generate a +dummy phase. In manual modes the guest-provided byte stream is authoritative. + +Avoiding double counting +------------------------ + +Exactly one side should generate each dummy byte transfer seen by the flash: + +* If the guest sends dummy bytes through the controller, the controller forwards + them and the flash consumes them. +* If the guest programs a controller dummy-cycle register, the controller + converts those cycles to dummy byte transfers and the flash consumes them. +* The flash may know that a command requires dummy bytes, but it does not create + transfers on the SSI bus. + +Do not implement controller-side snooping that watches manual-mode opcode +streams and injects extra dummy transfers based on flash opcodes. That mixes +flash command semantics into the controller and is fragile when flash models +gain correct dummy-byte accounting. + +Examples in the tree +-------------------- + +The following models illustrate the boundary: + +* ``hw/block/m25p80.c`` keeps fast-read dummy requirements as byte counts in + ``needed_bytes``. Manufacturer-specific helpers convert datasheet dummy + cycles to the byte stream expected by the model. +* ``hw/ssi/aspeed_smc.c`` generates dummy byte transfers for direct fast-read + mode from controller registers, but manual user-mode writes are forwarded as + guest-provided bytes. +* ``hw/ssi/npcm7xx_fiu.c`` converts the direct-read dummy configuration to the + number of dummy byte transfers sent before reading data. + +Review checklist +---------------- + +When adding or changing a SPI flash controller or flash model, check: + +* Are dummy counts stored in byte units when they drive flash state machines? +* If a hardware register stores cycles, is the conversion to bytes based on the + bus width of the dummy phase? +* Are manual guest-provided dummy bytes forwarded without extra injection? +* Are automatic controller-generated dummy phases modeled by the controller? +* Is flash-specific opcode knowledge kept in the flash model rather than copied + into controller snooping paths? From 5a811329bde78fc223abc384c85b0f62e3b34f7c Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Fri, 10 Jul 2026 03:21:39 +0800 Subject: [PATCH 009/860] hw/scsi/vmw_pvscsi: translate data endianness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch improves the implementation of the pvscsi device by translating the endianness of the data sent or received from the guest. This ensures pvscsi can work on big-endian hosts with little-endian guests. This patch assumes, although not having found any specifications, that the pvscsi device is little-endian, since pvscsi seems to be used only on x86 platforms, which are little-endian. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Miao Wang Message-ID: <20260710-pvscsi-endianness-v3-1-27fe1c4d1f6e@gmail.com> [PMD: Rebased on top of commit cb30b8758d4 physmem API conversion] Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/vmw_pvscsi.c | 57 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 4398aa5499..05f93171cd 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -392,9 +392,18 @@ static void pvscsi_cmp_ring_put(PVSCSIState *s, struct PVSCSIRingCmpDesc *cmp_desc) { hwaddr cmp_descr_pa; + PVSCSIRingCmpDesc cmp_desc_conv; cmp_descr_pa = pvscsi_ring_pop_cmp_descr(&s->rings); trace_pvscsi_cmp_ring_put(cmp_descr_pa); + cmp_desc_conv = (struct PVSCSIRingCmpDesc) { + .context = cpu_to_le64(cmp_desc->context), + .dataLen = cpu_to_le64(cmp_desc->dataLen), + .senseLen = cpu_to_le32(cmp_desc->senseLen), + .hostStatus = cpu_to_le16(cmp_desc->hostStatus), + .scsiStatus = cpu_to_le16(cmp_desc->scsiStatus), + }; + cmp_desc = &cmp_desc_conv; physical_memory_write(cmp_descr_pa, cmp_desc, sizeof(*cmp_desc)); } @@ -402,9 +411,18 @@ static void pvscsi_msg_ring_put(PVSCSIState *s, struct PVSCSIRingMsgDesc *msg_desc) { hwaddr msg_descr_pa; + PVSCSIRingMsgDesc msg_desc_conv; + int i; msg_descr_pa = pvscsi_ring_pop_msg_descr(&s->rings); trace_pvscsi_msg_ring_put(msg_descr_pa); + msg_desc_conv = (PVSCSIRingMsgDesc) { + .type = cpu_to_le32(msg_desc->type), + }; + for (i = 0; i < ARRAY_SIZE(msg_desc->args); i++) { + msg_desc_conv.args[i] = cpu_to_le32(msg_desc->args[i]); + } + msg_desc = &msg_desc_conv; physical_memory_write(msg_descr_pa, msg_desc, sizeof(*msg_desc)); } @@ -481,6 +499,9 @@ pvscsi_get_next_sg_elem(PVSCSISGState *sg) struct PVSCSISGElement elem; physical_memory_read(sg->elemAddr, &elem, sizeof(elem)); + elem.addr = le64_to_cpu(elem.addr); + elem.length = le32_to_cpu(elem.length); + elem.flags = le32_to_cpu(elem.flags); if ((elem.flags & ~PVSCSI_KNOWN_FLAGS) != 0) { /* * There is PVSCSI_SGE_FLAG_CHAIN_ELEMENT flag described in @@ -759,6 +780,12 @@ pvscsi_process_io(PVSCSIState *s) trace_pvscsi_process_io(next_descr_pa); physical_memory_read(next_descr_pa, &descr, sizeof(descr)); + descr.context = le64_to_cpu(descr.context); + descr.dataAddr = le64_to_cpu(descr.dataAddr); + descr.dataLen = le64_to_cpu(descr.dataLen); + descr.senseAddr = le64_to_cpu(descr.senseAddr); + descr.senseLen = le32_to_cpu(descr.senseLen); + descr.flags = le32_to_cpu(descr.flags); pvscsi_process_request_descriptor(s, &descr); } @@ -808,6 +835,17 @@ pvscsi_on_cmd_setup_rings(PVSCSIState *s) { PVSCSICmdDescSetupRings *rc = (PVSCSICmdDescSetupRings *) s->curr_cmd_data; + PVSCSICmdDescSetupRings translated; + int i; + + translated.reqRingNumPages = le32_to_cpu(rc->reqRingNumPages); + translated.cmpRingNumPages = le32_to_cpu(rc->cmpRingNumPages); + translated.ringsStatePPN = le64_to_cpu(rc->ringsStatePPN); + for (i = 0; i < PVSCSI_SETUP_RINGS_MAX_NUM_PAGES; i++) { + translated.reqRingPPNs[i] = le64_to_cpu(rc->reqRingPPNs[i]); + translated.cmpRingPPNs[i] = le64_to_cpu(rc->cmpRingPPNs[i]); + } + rc = &translated; trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_RINGS"); @@ -831,6 +869,11 @@ pvscsi_on_cmd_abort(PVSCSIState *s) PVSCSICmdDescAbortCmd *cmd = (PVSCSICmdDescAbortCmd *) s->curr_cmd_data; PVSCSIRequest *r, *next; + PVSCSICmdDescAbortCmd translated = *cmd; + translated.context = le32_to_cpu(cmd->context); + translated.target = le32_to_cpu(cmd->target); + cmd = &translated; + trace_pvscsi_on_cmd_abort(cmd->context, cmd->target); QTAILQ_FOREACH_SAFE(r, &s->pending_queue, next, next) { @@ -862,6 +905,10 @@ pvscsi_on_cmd_reset_device(PVSCSIState *s) (struct PVSCSICmdDescResetDevice *) s->curr_cmd_data; SCSIDevice *sdev; + PVSCSICmdDescResetDevice translated = *cmd; + translated.target = le32_to_cpu(cmd->target); + cmd = &translated; + sdev = pvscsi_device_find(s, 0, cmd->target, cmd->lun, &target_lun); trace_pvscsi_on_cmd_reset_dev(cmd->target, (int) target_lun, sdev); @@ -892,6 +939,14 @@ pvscsi_on_cmd_setup_msg_ring(PVSCSIState *s) { PVSCSICmdDescSetupMsgRing *rc = (PVSCSICmdDescSetupMsgRing *) s->curr_cmd_data; + PVSCSICmdDescSetupMsgRing translated = *rc; + int i; + + translated.numPages = le32_to_cpu(rc->numPages); + for (i = 0; i < PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES; i++) { + translated.ringPPNs[i] = le64_to_cpu(rc->ringPPNs[i]); + } + rc = &translated; trace_pvscsi_on_cmd_arrived("PVSCSI_CMD_SETUP_MSG_RING"); @@ -994,7 +1049,7 @@ pvscsi_on_command_data(PVSCSIState *s, uint32_t value) size_t bytes_arrived = s->curr_cmd_data_cntr * sizeof(uint32_t); assert(bytes_arrived < sizeof(s->curr_cmd_data)); - s->curr_cmd_data[s->curr_cmd_data_cntr++] = value; + s->curr_cmd_data[s->curr_cmd_data_cntr++] = cpu_to_le32(value); pvscsi_do_command_processing(s); } From ecc569639aeb5b702f1098d3eb5b28d3168604c8 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Fri, 10 Jul 2026 03:21:40 +0800 Subject: [PATCH 010/860] hw/scsi/vmw_pvscsi: add a comment to explain the endianness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a comment to explain the endianness of the pvscsi device. We have no information about the endianness should be little-endian or CPU native endian because the current driver code is designed to work only on x86 and is not endianness aware. We assume that the pvscsi device is implicitly little-endian. Signed-off-by: Miao Wang Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260710-pvscsi-endianness-v3-2-27fe1c4d1f6e@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/vmw_pvscsi.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hw/scsi/vmw_pvscsi.h b/hw/scsi/vmw_pvscsi.h index a3ae517e19..defff9e179 100644 --- a/hw/scsi/vmw_pvscsi.h +++ b/hw/scsi/vmw_pvscsi.h @@ -108,6 +108,20 @@ enum PVSCSICommands { #define PVSCSI_COMMAND_PROCESSING_FAILED (-1) #define PVSCSI_COMMAND_NOT_ENOUGH_DATA (-2) +/* + * About endianess for the below structs: + * + * These structs are used to describe the data that is exchanged between the + * guest and the PVSCSI device. The endianess of the fields in these structs + * is not defined by any standard. The current implemented drivers are designed + * to only work on x86 architecture, so there is no endianess awareness in the + * drivers and thus we have no idea whether the fields should be in little- + * endian or target native endian format. + * + * Considering the above, we assume that PVSCSI is implicitly little-endian and + * expect the fields in these structs to be in little-endian format. + */ + /* * Command descriptor for PVSCSI_CMD_RESET_DEVICE -- */ From a3cc0069e151e5eb5db57bb4e86b00861d5b97ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 10 Jul 2026 17:43:52 +0400 Subject: [PATCH 011/860] hw/display/qxl: fix TOCTOU in cursor chunk data_size handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Snapshot chunk.data_size into a host-local variable before passing it to qxl_phys2virt() for validation, and pass it through qxl_cursor() and qxl_unpack_chunks() so that no subsequent code re-reads the field. Without this, a racing vCPU can inflate data_size between the qxl_phys2virt() validation and the memcpy in qxl_unpack_chunks(), causing a source read past the validated region. In practice the read stays within the guest's own VRAM mmap, so the impact is limited. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3757 Reported-by: Feifan Qian Signed-off-by: Marc-Andre Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260710134352.2313675-1-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/qxl-render.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 3bf634ee05..4799c9e8be 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -217,7 +217,8 @@ void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) } static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, - QXLDataChunk *chunk, uint32_t group_id) + QXLDataChunk *chunk, uint32_t group_id, + uint32_t chunk_data_size) { uint32_t max_chunks = 32; size_t offset = 0; @@ -225,22 +226,21 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, QXLPHYSICAL next_chunk_phys = 0; for (;;) { - bytes = MIN(size - offset, chunk->data_size); + bytes = MIN(size - offset, chunk_data_size); memcpy(dest + offset, chunk->data, bytes); offset += bytes; if (offset == size) { return; } 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_data_size = chunk->data_size; chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, - sizeof(QXLDataChunk) + chunk->data_size); + sizeof(QXLDataChunk) + chunk_data_size); if (!chunk) { return; } @@ -252,7 +252,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, } static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, - uint32_t group_id) + uint32_t group_id, uint32_t chunk_data_size) { QEMUCursor *c; uint8_t *and_mask, *xor_mask; @@ -272,11 +272,11 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, case SPICE_CURSOR_TYPE_MONO: /* Assume that the full cursor is available in a single chunk. */ size = 2 * cursor_get_mono_bpl(c) * c->height; - if (size != cursor->data_size || cursor->chunk.data_size < size) { + if (size != cursor->data_size || chunk_data_size < size) { qxl_set_guest_bug(qxl, "%s: bad monochrome cursor %ux%u" " data_size %u chunk_size %u", __func__, c->width, c->height, - cursor->data_size, cursor->chunk.data_size); + cursor->data_size, chunk_data_size); goto fail; } and_mask = cursor->chunk.data; @@ -288,7 +288,8 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, break; case SPICE_CURSOR_TYPE_ALPHA: size = sizeof(uint32_t) * c->width * c->height; - qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id); + qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id, + chunk_data_size); if (qxl->debug > 2) { cursor_print_ascii_art(c, "qxl/alpha"); } @@ -325,19 +326,23 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) } switch (cmd->type) { case QXL_CURSOR_SET: + { + uint32_t chunk_data_size; + /* First read the QXLCursor to get QXLDataChunk::data_size ... */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, sizeof(QXLCursor)); if (!cursor) { return 1; } + chunk_data_size = cursor->chunk.data_size; /* Then read including the chunked data following QXLCursor. */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, - sizeof(QXLCursor) + cursor->chunk.data_size); + sizeof(QXLCursor) + chunk_data_size); if (!cursor) { return 1; } - c = qxl_cursor(qxl, cursor, ext->group_id); + c = qxl_cursor(qxl, cursor, ext->group_id, chunk_data_size); if (c == NULL) { c = cursor_builtin_left_ptr(); } @@ -351,6 +356,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) qemu_mutex_unlock(&qxl->ssd.lock); qemu_bh_schedule(qxl->ssd.cursor_bh); break; + } case QXL_CURSOR_MOVE: qemu_mutex_lock(&qxl->ssd.lock); qxl->ssd.mouse_x = cmd->u.position.x; From 76dbe26fd6500a94b85bf0f3a39d73b72f5fab7b Mon Sep 17 00:00:00 2001 From: Dmitry Pimenov Date: Sat, 11 Jul 2026 00:23:50 +0200 Subject: [PATCH 012/860] hw/sparc64/niagara: use int64_t for vdisk size to avoid truncation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit blk_getlength() returns int64_t, but niagara_init() stored it in an int, truncating the if=pflash virtual-ramdisk size for images >= 2 GiB. A ~4 GiB image truncated to 0/negative, failed the `size > 0` check, and exit(1)'d before the CPU ran, ending with: qemu-system-sparc64: could not load ram disk Signed-off-by: Dmitry Pimenov Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260710222350.9185-1-sun4qemu@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sparc64/niagara.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 1211ecb82d..ad5e5945a8 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -137,7 +137,7 @@ static void niagara_init(MachineState *machine) outside of the partition RAM */ if (dinfo) { BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - int size = blk_getlength(blk); + int64_t size = blk_getlength(blk); if (size > 0) { memory_region_init_ram(&s->vdisk_ram, NULL, "sun4v_vdisk.ram", size, &error_fatal); From 564c4d7e99d87b39c3faea52ef8759df7f786f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 13 Jul 2026 16:56:22 +0400 Subject: [PATCH 013/860] hw/display/virtio-gpu: fix dmabuf_fd leak on remap failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When virtio_gpu_create_udmabuf() succeeds but virtio_gpu_remap_udmabuf() fails (mmap returns MAP_FAILED), virtio_gpu_init_udmabuf() returns early without closing the dmabuf fd. Since res->blob is never set in this path, later cleanup via virtio_gpu_cleanup_mapping() skips virtio_gpu_fini_udmabuf() entirely, leaking the file descriptor. Call virtio_gpu_destroy_udmabuf() before the early return to close the fd. This function already handles partial state correctly: it skips the munmap when res->remapped is NULL and closes the fd when res->dmabuf_fd >= 0. Fixes: 9b60cdf98723 ("virtio-gpu: Add udmabuf helpers") Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Dmitry Osipenko Reviewed-by: Akihiko Odaki Message-ID: <20260713125622.111513-1-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/virtio-gpu-udmabuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index d5ac1cfca0..5f08c855dd 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -143,6 +143,7 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) } virtio_gpu_remap_udmabuf(res); if (!res->remapped) { + virtio_gpu_destroy_udmabuf(res); return; } pdata = res->remapped; From 98e5a8eb4fb9145e9dc336b297ec14390ce88ff9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 13 Jul 2026 18:04:58 +0200 Subject: [PATCH 014/860] hw/usb/hcd-ohci: Make sure that ohci_service_ed_list() cannot loop forever MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The inner while loop in ohci_service_ed_list() could theoretically loop forever if a malicious guest prepares a set of bad descriptors. Add a check to the loop to avoid this situation. Reported-by: Feifan Qian Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3781 Signed-off-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Message-ID: <20260713160458.343323-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ohci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 40ebafb4dd..c4a9741bd4 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -28,6 +28,7 @@ #include "qemu/osdep.h" #include "hw/core/irq.h" #include "qapi/error.h" +#include "qemu/log.h" #include "qemu/module.h" #include "qemu/timer.h" #include "hw/usb/usb.h" @@ -1129,6 +1130,8 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) return 0; } for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { + unsigned int ed_cnt = 0; + if (ohci_read_ed(ohci, cur, &ed)) { trace_usb_ohci_ed_read_error(cur); ohci_die(ohci); @@ -1172,6 +1175,13 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head) break; } } + + if (ed_cnt++ > ED_LINK_LIMIT) { + qemu_log_mask(LOG_GUEST_ERROR, + "ohci: Too many endpoint descriptors in loop\n"); + ohci_die(ohci); + return 0; + } } if (ohci_put_ed(ohci, cur, &ed)) { From 5257259428eaa305b76e63e5657448a39de0570f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 13 Jul 2026 18:14:04 +0200 Subject: [PATCH 015/860] hw/usb/hcd-xhci: Turn guest-triggerable abort() into qemu_log_mask() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FIXME macros in xhci_alloc_device_streams() can be triggered by a (malicious) guest. Since the macro also contains an abort() statement, this terminates QEMU. Turn the FIXME statements into a qemu_log_mask() instead to avoid that a guest can shoot itself this way. Reported-by: Feifan Qian Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3784 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20260713161406.361197-2-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 2cdab3ba0e..f5bb7d2560 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -965,11 +965,13 @@ static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid, * together and make an usb_device_alloc_streams call per group. */ if (epctxs[i]->nr_pstreams != req_nr_streams) { - FIXME("guest streams config not identical for all eps"); + qemu_log_mask(LOG_UNIMP, + "guest streams config not identical for all eps\n"); return CC_RESOURCE_ERROR; } if (eps[i]->max_streams != dev_max_streams) { - FIXME("device streams config not identical for all eps"); + qemu_log_mask(LOG_UNIMP, + "device streams config not identical for all eps\n"); return CC_RESOURCE_ERROR; } } From 96c9f9709d66204b485e669cfc465c6201519b76 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 13 Jul 2026 18:14:05 +0200 Subject: [PATCH 016/860] hw/usb/hcd-xhci: Remove the FIXME macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FIXME macro is only used in one case, which should hopefully never trigger: The containing function handles all the USB_RET_* values except for USB_RET_ADD_TO_QUEUE and USB_RET_REMOVE_FROM_QUEUE, which are both internal return values for when an async packet needs to be queued or dequeued, and which shouldn't still be the status by the time we get to this function. Thus let's simplify this spot and use a g_assert_not_reached() instead (and remove the DPRINT() in front of it to avoid that code analyzers trip over unreachable code here). Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20260713161406.361197-3-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index f5bb7d2560..cc61e28d33 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -39,8 +39,6 @@ #else #define DPRINTF(...) do {} while (0) #endif -#define FIXME(_msg) do { fprintf(stderr, "FIXME %s:%d %s\n", \ - __func__, __LINE__, _msg); abort(); } while (0) #define TRB_LINK_LIMIT 32 #define COMMAND_LIMIT 256 @@ -1673,9 +1671,7 @@ static int xhci_try_complete_packet(XHCITransfer *xfer) xhci_stall_ep(xfer); break; default: - DPRINTF("%s: FIXME: status = %d\n", __func__, - xfer->packet.status); - FIXME("unhandled USB_RET_*"); + g_assert_not_reached(); } return 0; } From ebe66684a58587bcaf91055fa92f25f2a5d175e9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 13 Jul 2026 18:14:06 +0200 Subject: [PATCH 017/860] hw/usb/hcd-xhci: Use qemu_log_mask() instead of fprintf() statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've got a proper way for logging unimplemented hardware features, so use qemu_log_mask() instead of the fprintf() here now. Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260713161406.361197-4-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index cc61e28d33..c7e050e38f 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1017,7 +1017,8 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, } sctx = epctx->pstreams + streamid; } else { - fprintf(stderr, "xhci: FIXME: secondary streams not implemented yet"); + qemu_log_mask(LOG_UNIMP, + "xhci: secondary streams not implemented yet\n"); *cc_error = CC_INVALID_STREAM_TYPE_ERROR; return NULL; } From 504f2bad932c4f1b067d3b759597ed38e57576a1 Mon Sep 17 00:00:00 2001 From: Rohitashv Kumar Date: Mon, 13 Jul 2026 19:45:11 +0000 Subject: [PATCH 018/860] net: only advertise passt in netdev help when CONFIG_PASST MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit show_netdevs() lists each conditionally-compiled netdev backend behind its CONFIG_* guard (CONFIG_SLIRP for "user", CONFIG_L2TPV3 for "l2tpv3", CONFIG_NET_BRIDGE for "bridge", ...). The "passt" entry was added unconditionally, so "-netdev help" advertises passt even in builds configured with --disable-passt. Trying to use it then fails with "Parameter 'type' does not accept value 'passt'", since the QAPI NetClientDriver enum member "passt" is gated by 'if': 'CONFIG_PASST'. Guard the help entry with CONFIG_PASST so the advertised backends match those actually compiled into the binary. Signed-off-by: Rohitashv Kumar Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Puranjay Mohan Message-ID: <20260713194511.1058450-1-rohit.kuma1313@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- net/net.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/net.c b/net/net.c index 2892f1730d..5c39f8e7b4 100644 --- a/net/net.c +++ b/net/net.c @@ -1385,7 +1385,9 @@ void show_netdevs(void) "dgram", "hubport", "tap", +#ifdef CONFIG_PASST "passt", +#endif #ifdef CONFIG_SLIRP "user", #endif From c0c8990a2bbd89fc3986d01697f156576c5c0984 Mon Sep 17 00:00:00 2001 From: Harry van Haaren Date: Thu, 16 Jul 2026 09:41:42 +0000 Subject: [PATCH 019/860] plugins/execlog: fix segfault/race-cond on per-vCPU structures The existing code in execlog was never upgraded to the Scoreboard API, resulting in a bespoke implementation of per-vCPU datastructure handling. This had some race-conditions, and causes segfaults with a simple multi-threaded program and two instances of execlog running. The patch here refactors the custom GArray and GRWLock code away, and uses the scoreboard APIs like the other plugins. This solves the "printing while expanding" race-condition of two plugins with multiple threads in the guest, and hence fixes a segfault. Output remains atomic per instruction by building the full line (including the trailing newline) in the per-vCPU GString before making a single qemu_plugin_outs() call, relying on QEMU's own log locking rather than an additional mutex. Reviewed-by: Pierrick Bouvier Tested-by: Pierrick Bouvier Signed-off-by: Harry van Haaren Link: https://lore.kernel.org/qemu-devel/20260716094126.787556-2-harry.vanhaaren@openchip.com Signed-off-by: Pierrick Bouvier --- contrib/plugins/execlog.c | 68 +++++++++++++-------------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index 74325495cc..dfe00bf836 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -31,8 +31,12 @@ typedef struct CPU { QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; -static GArray *cpus; -static GRWLock expand_array_lock; +/* + * Per-vCPU state stored in a qemu_plugin_scoreboard. The scoreboard manages + * per-vCPU storage automatically, eliminating the need for manual array + * growth, locks, or pointer-stability workarounds. + */ +static struct qemu_plugin_scoreboard *cpus; static GPtrArray *imatches; static GArray *amatches; @@ -41,23 +45,13 @@ static bool disas_assist; static GMutex add_reg_name_lock; static GPtrArray *all_reg_names; -static CPU *get_cpu(int vcpu_index) -{ - CPU *c; - g_rw_lock_reader_lock(&expand_array_lock); - c = &g_array_index(cpus, CPU, vcpu_index); - g_rw_lock_reader_unlock(&expand_array_lock); - - return c; -} - /** * Add memory read or write information to current instruction log */ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t info, uint64_t vaddr, void *udata) { - CPU *c = get_cpu(cpu_index); + CPU *c = qemu_plugin_scoreboard_find(cpus, cpu_index); GString *s = c->last_exec; /* Find vCPU in array */ @@ -117,7 +111,7 @@ static void insn_check_regs(CPU *cpu) /* Log last instruction while checking registers */ static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata) { - CPU *cpu = get_cpu(cpu_index); + CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index); /* Print previous instruction in cache */ if (cpu->last_exec->len) { @@ -125,8 +119,8 @@ static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata) insn_check_regs(cpu); } + g_string_append_c(cpu->last_exec, '\n'); qemu_plugin_outs(cpu->last_exec->str); - qemu_plugin_outs("\n"); } /* Store new instruction in cache */ @@ -138,7 +132,7 @@ static void vcpu_insn_exec_with_regs(unsigned int cpu_index, void *udata) /* Log last instruction while checking registers, ignore next */ static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata) { - CPU *cpu = get_cpu(cpu_index); + CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index); /* Print previous instruction in cache */ if (cpu->last_exec->len) { @@ -146,8 +140,8 @@ static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata) insn_check_regs(cpu); } + g_string_append_c(cpu->last_exec, '\n'); qemu_plugin_outs(cpu->last_exec->str); - qemu_plugin_outs("\n"); } /* reset */ @@ -157,12 +151,12 @@ static void vcpu_insn_exec_only_regs(unsigned int cpu_index, void *udata) /* Log last instruction without checking regs, setup next */ static void vcpu_insn_exec(unsigned int cpu_index, void *udata) { - CPU *cpu = get_cpu(cpu_index); + CPU *cpu = qemu_plugin_scoreboard_find(cpus, cpu_index); /* Print previous instruction in cache */ if (cpu->last_exec->len) { + g_string_append_c(cpu->last_exec, '\n'); qemu_plugin_outs(cpu->last_exec->str); - qemu_plugin_outs("\n"); } /* Store new instruction in cache */ @@ -378,40 +372,28 @@ static GPtrArray *registers_init(int vcpu_index) * - last_exec tracking data * - list of tracked registers * - initial value of registers - * - * As we could have multiple threads trying to do this we need to - * serialise the expansion under a lock. */ static void vcpu_init(unsigned int vcpu_index, void *userdata) { - CPU *c; - - g_rw_lock_writer_lock(&expand_array_lock); - if (vcpu_index >= cpus->len) { - g_array_set_size(cpus, vcpu_index + 1); - } - g_rw_lock_writer_unlock(&expand_array_lock); - - c = get_cpu(vcpu_index); + CPU *c = qemu_plugin_scoreboard_find(cpus, vcpu_index); c->last_exec = g_string_new(NULL); c->registers = registers_init(vcpu_index); } /** - * On plugin exit, print last instruction in cache + * On plugin exit, flush any remaining cached instructions and free state. */ static void plugin_exit(void *p) { - guint i; - g_rw_lock_reader_lock(&expand_array_lock); - for (i = 0; i < cpus->len; i++) { - CPU *c = get_cpu(i); - if (c->last_exec && c->last_exec->str) { + int n = qemu_plugin_num_vcpus(); + for (int i = 0; i < n; i++) { + CPU *c = qemu_plugin_scoreboard_find(cpus, i); + if (c->last_exec && c->last_exec->len) { + g_string_append_c(c->last_exec, '\n'); qemu_plugin_outs(c->last_exec->str); - qemu_plugin_outs("\n"); } } - g_rw_lock_reader_unlock(&expand_array_lock); + qemu_plugin_scoreboard_free(cpus); } /* Add a match to the array of matches */ @@ -452,12 +434,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - /* - * Initialize dynamic array to cache vCPU instruction. In user mode - * we don't know the size before emulation. - */ - cpus = g_array_sized_new(true, true, sizeof(CPU), - info->system_emulation ? info->system.max_vcpus : 1); + /* Initialize scoreboard to cache per-vCPU instruction state. */ + cpus = qemu_plugin_scoreboard_new(sizeof(CPU)); for (int i = 0; i < argc; i++) { char *opt = argv[i]; From fdff1c864aaab3c2458c2d6802a5cfe9dfcbdff4 Mon Sep 17 00:00:00 2001 From: Harry van Haaren Date: Thu, 16 Jul 2026 09:41:53 +0000 Subject: [PATCH 020/860] plugins/execlog: fix execlog vcpu_exit execution print loss Executed instructions are cached in string format inside the execlog plugin. These strings are flushed on exit of a TB, improving performance. This causes executed instructions to be lost when an 'ecall' (riscv system call) occurs that causes the thread to terminate. The fix in this patch registers a 'vcpu_exit' callback, and flushes any content in the c->last_exec buffer, to ensure all instructions are present in the final instruction log. Reviewed-by: Pierrick Bouvier Tested-by: Pierrick Bouvier Signed-off-by: Harry van Haaren Link: https://lore.kernel.org/qemu-devel/20260716094126.787556-3-harry.vanhaaren@openchip.com Signed-off-by: Pierrick Bouvier --- contrib/plugins/execlog.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index dfe00bf836..cb0bd399d8 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -380,6 +380,25 @@ static void vcpu_init(unsigned int vcpu_index, void *userdata) c->registers = registers_init(vcpu_index); } +/** + * On vCPU exit, flush the last cached instruction for this vCPU. + * + * The one-instruction-delay pattern stores each instruction in last_exec and + * only prints it when the *next* callback fires. When a thread exits via + * syscall (e.g. ecall/exit), no subsequent callback fires for that vCPU and + * the final instruction is silently dropped. Flushing here guarantees it is + * written before the vCPU is torn down. + */ +static void vcpu_exit(unsigned int vcpu_index, void *udata) +{ + CPU *c = qemu_plugin_scoreboard_find(cpus, vcpu_index); + if (c->last_exec && c->last_exec->len) { + g_string_append_c(c->last_exec, '\n'); + qemu_plugin_outs(c->last_exec->str); + g_string_truncate(c->last_exec, 0); + } +} + /** * On plugin exit, flush any remaining cached instructions and free state. */ @@ -461,6 +480,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, /* Register init, translation block and exit callbacks */ qemu_plugin_register_vcpu_init_cb(id, vcpu_init, NULL); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans, NULL); + qemu_plugin_register_vcpu_exit_cb(id, vcpu_exit, NULL); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); return 0; From c4ce04cfb7460d46c0262d437a7f35bb5c5fc449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 16 Jul 2026 11:25:15 +0100 Subject: [PATCH 021/860] tests/docker: ensure container command is probed at most once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The '--engine' arg accepts either 'podman' or 'docker', which is not sufficiently granular to map directly to a command. This means that docker.py still has to then probe the exact command to use. Meanwhile the 'probe' command prints out the full command to use but this cannot be passed back to docker.py to avoid probing again, so the caching is only useful in the few case where we run a container directly bypassing docker.py. Address this by replacing --engine with --command for docker.py. This in turn requires the --container-engine configure arg to be replaced with --container-command. With these changes the container command is probed at most once during configure and never again, while running in an unconfigured tree will still probe on demand. Tested-by: Pierrick Bouvier Reviewed-by: Pierrick Bouvier Signed-off-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20260716102515.1503404-1-berrange@redhat.com Signed-off-by: Pierrick Bouvier --- configure | 25 +++++++------ tests/docker/Makefile.include | 13 ++++--- tests/docker/docker.py | 67 ++++++++++++----------------------- 3 files changed, 40 insertions(+), 65 deletions(-) diff --git a/configure b/configure index d8bc10060e..6ddfe4b534 100755 --- a/configure +++ b/configure @@ -172,7 +172,7 @@ fi # some defaults, based on the host environment # default parameters -container_engine="auto" +container_command="" cpu="" cross_compile="no" cross_prefix="" @@ -734,7 +734,7 @@ for opt do ;; --disable-containers) use_containers="no" ;; - --container-engine=*) container_engine="$optarg" + --container-command=*) container_command="$optarg" ;; --rust-target-triple=*) rust_target_triple="$optarg" ;; @@ -869,7 +869,7 @@ Advanced options (experts only): --enable-debug enable common debug build options --cpu=CPU Build for host CPU [$cpu] --disable-containers don't use containers for cross-building - --container-engine=TYPE which container engine to use [$container_engine] + --container-command=CMD which container command to use [autodetect] --gdb=GDB-path gdb to use for gdbstub tests [$gdb_bin] --wasm64-32bit-address-limit Restrict wasm64 address space to 32-bit (default is to use the whole 64-bit range). @@ -1291,12 +1291,12 @@ fi ########################################## # functions to probe cross compilers -runc="no" -if test $use_containers = "yes" && (has "docker" || has "podman"); then - runc=$($python "$source_path"/tests/docker/docker.py --engine "$container_engine" probe) - if test "$runc" != "no"; then - docker_py="$python $source_path/tests/docker/docker.py --engine $container_engine" - fi +if test "$container_command" = ""; then + container_command=$($python "$source_path"/tests/docker/docker.py probe) + test "$container_command" = "no" && container_command="" +fi +if test $use_containers = "yes" && test "$container_command" != ""; then + docker_py="$python $source_path/tests/docker/docker.py --command $container_command" fi # cross compilers defaults, can be overridden with --cross-cc-ARCH @@ -1415,7 +1415,7 @@ probe_target_compiler() { esac for host in $container_hosts; do - test "$runc" != no || continue + test "$container_command" != "" || continue test "$host" = "$cpu" || continue case $target_arch in # debian-all-test-cross architectures @@ -1736,9 +1736,8 @@ echo all: >> $config_host_mak echo "SRC_PATH=$source_path" >> $config_host_mak echo "TARGET_DIRS=$target_list" >> $config_host_mak echo "GDB=$gdb_bin" >> $config_host_mak -if test "$runc" != no; then - echo "RUNC=$runc" >> $config_host_mak - echo "CONTAINER_ENGINE=$container_engine" >> $config_host_mak +if test "$container_command" != ""; then + echo "CONTAINER_COMMAND=$container_command" >> $config_host_mak fi echo "SUBDIRS=$subdirs" >> $config_host_mak if test "$rust" != disabled; then diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 4725c39807..0adddb6a5c 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -16,9 +16,8 @@ DOCKER_DEFAULT_REGISTRY := registry.gitlab.com/qemu-project/qemu endif DOCKER_REGISTRY := $(if $(REGISTRY),$(REGISTRY),$(DOCKER_DEFAULT_REGISTRY)) -CONTAINER_ENGINE = auto -DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --engine $(CONTAINER_ENGINE) -RUNC ?= $(shell $(DOCKER_SCRIPT) probe) +CONTAINER_COMMAND ?= $(shell $(SRC_PATH)/tests/docker/docker.py probe) +DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py --command "$(CONTAINER_COMMAND)" CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$) DOCKER_SRC_COPY := $(BUILD_DIR)/docker-src.$(CUR_TIME) @@ -41,7 +40,7 @@ docker-qemu-src: $(DOCKER_SRC_COPY) # General rule for building docker images. docker-image-%: $(DOCKER_FILES_DIR)/%.docker $(call quiet-command, \ - DOCKER_BUILDKIT=1 $(RUNC) build \ + DOCKER_BUILDKIT=1 $(CONTAINER_COMMAND) build \ $(if $(DOCKER_V),,--quiet) \ $(if $(NOCACHE),--no-cache, \ $(if $(DOCKER_REGISTRY),--cache-from $(DOCKER_REGISTRY)/qemu/$*)) \ @@ -152,7 +151,7 @@ $(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES)), \ ) docker: - @echo 'Build QEMU and run tests inside $(RUNC) containers' + @echo 'Build QEMU and run tests inside $(CONTAINER_COMMAND) containers' @echo @echo 'Available targets:' @echo @@ -219,10 +218,10 @@ docker-run: docker-qemu-src $(IMAGE) --executable $(EXECUTABLE), \ " COPYING $(EXECUTABLE) to $(IMAGE)")) $(call quiet-command, \ - $(RUNC) run \ + $(CONTAINER_COMMAND) run \ --rm \ $(if $(NOUSER),, \ - $(if $(filter docker,$(RUNC)), \ + $(if $(filter docker,$(CONTAINER_COMMAND)), \ -u $(UID), \ --userns keep-id \ ) \ diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 9e18b984f4..d2f39b5645 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -35,27 +35,6 @@ FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] DEVNULL = open(os.devnull, 'wb') -class EngineEnum(enum.IntEnum): - AUTO = 1 - DOCKER = 2 - PODMAN = 3 - - def __str__(self): - return self.name.lower() - - def __repr__(self): - return str(self) - - @staticmethod - def argparse(s): - try: - return EngineEnum[s.upper()] - except KeyError: - return s - - -USE_ENGINE = EngineEnum.AUTO - def _bytes_checksum(bytes): """Calculate a digest string unique to the text content""" return hashlib.sha1(bytes).hexdigest() @@ -73,12 +52,11 @@ def _file_checksum(filename): def _guess_engine_command(): """ Guess a working engine command or raise exception if not found""" - commands = [] - - if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.PODMAN]: - commands += [["podman"], ["podman-remote"], ["podman", "--remote"]] - if USE_ENGINE in [EngineEnum.AUTO, EngineEnum.DOCKER]: - commands += [["docker"], ["sudo", "-n", "docker"]] + commands = [["podman"], + ["podman-remote"], + ["podman", "--remote"], + ["docker"], + ["sudo", "-n", "docker"]] for cmd in commands: try: # 'version' is not sufficient to prove a working binary @@ -222,8 +200,11 @@ def _dockerfile_verify_flat(df): class Docker(object): """ Running Docker commands """ - def __init__(self): - self._command = _guess_engine_command() + def __init__(self, commandstr=None): + if commandstr is None: + self._command = _guess_engine_command() + else: + self._command = commandstr.split(" ") if ("docker" in self._command and "TRAVIS" not in os.environ and @@ -411,8 +392,8 @@ class RunCommand(SubCommand): help="Run container using the current user's uid") def run(self, args, argv): - return Docker().run(argv, args.keep, quiet=args.quiet, - as_user=args.run_as_current_user) + return Docker(args.command).run(argv, args.keep, quiet=args.quiet, + as_user=args.run_as_current_user) class BuildCommand(SubCommand): @@ -445,7 +426,7 @@ class BuildCommand(SubCommand): dockerfile = _read_dockerfile(args.dockerfile) tag = args.tag - dkr = Docker() + dkr = Docker(args.command) if "--no-cache" not in argv and \ dkr.image_matches_dockerfile(tag, dockerfile): if not args.quiet: @@ -512,7 +493,7 @@ class FetchCommand(SubCommand): help="Docker registry") def run(self, args, argv): - dkr = Docker() + dkr = Docker(args.command) dkr.command(cmd="pull", quiet=args.quiet, argv=["%s/%s" % (args.registry, args.tag)]) dkr.command(cmd="tag", quiet=args.quiet, @@ -590,7 +571,7 @@ class UpdateCommand(SubCommand): tmp.seek(0) # Run the build with our tarball context - dkr = Docker() + dkr = Docker(args.command) dkr.update_image(args.tag, tmp, quiet=args.quiet) return 0 @@ -601,7 +582,7 @@ class CleanCommand(SubCommand): name = "clean" def run(self, args, argv): - Docker().clean() + Docker(args.command).clean() return 0 @@ -610,7 +591,7 @@ class ImagesCommand(SubCommand): name = "images" def run(self, args, argv): - return Docker().command("images", argv, args.quiet) + return Docker(args.command).command("images", argv, args.quiet) class ProbeCommand(SubCommand): @@ -619,7 +600,7 @@ class ProbeCommand(SubCommand): def run(self, args, argv): try: - docker = Docker() + docker = Docker(args.command) print(" ".join(docker._command)) except Exception: print("no") @@ -651,18 +632,16 @@ class CcCommand(SubCommand): cmd += ["-v", "%s:%s:ro,z" % (p, p)] cmd += [args.image, args.cc] cmd += argv - return Docker().run(cmd, False, quiet=args.quiet, - as_user=True) + return Docker(args.command).run(cmd, False, quiet=args.quiet, + as_user=True) def main(): - global USE_ENGINE - parser = argparse.ArgumentParser(description="A Docker helper", usage="%s ..." % os.path.basename(sys.argv[0])) - parser.add_argument("--engine", type=EngineEnum.argparse, choices=list(EngineEnum), - help="specify which container engine to use") + parser.add_argument("--command", + help="specify which container engine command to use") subparsers = parser.add_subparsers(title="subcommands", help=None) for cls in SubCommand.__subclasses__(): cmd = cls() @@ -671,8 +650,6 @@ def main(): cmd.args(subp) subp.set_defaults(cmdobj=cmd) args, argv = parser.parse_known_args() - if args.engine: - USE_ENGINE = args.engine return args.cmdobj.run(args, argv) From 40cdadfefda3b930a64e5db0aae220bd00ce4ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 6 Jul 2026 12:54:21 +0400 Subject: [PATCH 022/860] i386/tdx: fix uninitialized variable warning in tdx_check_features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tdx_fetch_cpuid() only sets the output ret parameter on the error path. GCC cannot prove that r is always initialized before use in the caller, triggering -Werror=maybe-uninitialized. Initialize r to -1 to silence the warning. Fixes: 228e40f33048 ("i386/tdx: Fetch and validate CPUID of TD guest") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-Andre Lureau --- target/i386/kvm/tdx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index c9c3b05d5f..8294dbde3a 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -887,7 +887,7 @@ static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs) FeatureWordInfo *wi; FeatureWord w; bool mismatch = false; - int r; + int r = -1; fetch_cpuid = tdx_fetch_cpuid(cs, &r); if (!fetch_cpuid) { From 3543c2b855cc8cd25a5dbf05564a47ba42f45fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 6 Jul 2026 12:45:31 +0400 Subject: [PATCH 023/860] ui/vnc: fix OOB write in vnc_refresh_lossy_rect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vnc_refresh_lossy_rect() always marks a full VNC_STAT_RECT (64) rows as dirty when refreshing a lossy tile. When the display height is not a multiple of VNC_STAT_RECT (e.g. VNC_MAX_HEIGHT = 2160), the bottom tile is partial -- the last tile at y=2112 has only 48 valid rows. The unclamped loop writes to vs->dirty[2160..2175], past the end of the VNC_MAX_HEIGHT-sized array. Clamp the row count to the actual surface height so partial bottom tiles only mark valid dirty bitmap entries. Fixes: CVE-2026-48002 Fixes: 7d964c9d2fc6 ("vnc: refresh lossy rect after a given timeout") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3950 Reported-by: huntr bubble Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-Andre Lureau --- ui/vnc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/vnc.c b/ui/vnc.c index b2b69923b7..559d3954b8 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3004,10 +3004,18 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) int sty = y / VNC_STAT_RECT; int stx = x / VNC_STAT_RECT; int has_dirty = 0; + int height = MIN(pixman_image_get_height(vd->guest.fb), + pixman_image_get_height(vd->server)); + int rows; y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); + rows = MIN(VNC_STAT_RECT, height - y); + if (rows <= 0) { + return 0; + } + QTAILQ_FOREACH(vs, &vd->clients, next) { VncConnection *vc = container_of(vs, VncConnection, vs); int j; @@ -3022,7 +3030,7 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) } vc->worker.lossy_rect[sty][stx] = 0; - for (j = 0; j < VNC_STAT_RECT; ++j) { + for (j = 0; j < rows; ++j) { bitmap_set(vs->dirty[y + j], x / VNC_DIRTY_PIXELS_PER_BIT, VNC_STAT_RECT / VNC_DIRTY_PIXELS_PER_BIT); From fb71c895d7ff3c68f228095b5e6494e6c23b6a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 6 Jul 2026 14:40:39 +0400 Subject: [PATCH 024/860] ui/vnc: validate color shifts in SetPixelFormat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A malicious VNC client can send a SetPixelFormat message with shift values >= 32, causing UB mask computation (e.g. red_max << red_shift where red_shift is 255). Apparently, this is not covered by -fwrapv. Reject color shifts >= bits_per_pixel || 32 before computing masks. Fixes: 9f64916da20 ("pixman/vnc: use pixman images in vnc.") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3948 Reported-by: huntr bubble Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-Andre Lureau --- ui/vnc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/vnc.c b/ui/vnc.c index 559d3954b8..9219f593d9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2276,6 +2276,13 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, return; } + if (red_shift >= bits_per_pixel || red_shift >= 32 || + green_shift >= bits_per_pixel || green_shift >= 32 || + blue_shift >= bits_per_pixel || blue_shift >= 32) { + vnc_client_error(vs); + return; + } + vs->client_pf.rmax = red_max ? red_max : 0xFF; vs->client_pf.rbits = ctpopl(red_max); vs->client_pf.rshift = red_shift; From 3162692a3b67603e2b01f0c6441daa682acd6164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 6 Jul 2026 14:52:31 +0400 Subject: [PATCH 025/860] ui/vnc: use RFB wire types for client message handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use exact-width unsigned types for the static functions that process RFB client messages, matching the types returned by read_u8(), read_u16(), and read_u32(): - set_pixel_format: uint8_t/uint16_t for pixel format fields - pointer_event: uint8_t button_mask, uint16_t x/y - key_event/ext_key_event: bool down, uint32_t sym/keycode - do_key_event: uint32_t sym - framebuffer_update_request: uint8_t incremental, uint16_t x/y/w/h Drop needless declarations. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-Andre Lureau --- ui/vnc.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 9219f593d9..8d91cee4d9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -608,15 +608,7 @@ bool vnc_display_reload_certs(const char *id, Error **errp) 3) resolutions > 1024 */ -static int vnc_update_client(VncState *vs, int has_dirty); -static void vnc_disconnect_start(VncState *vs); - static void vnc_colordepth(VncState *vs); -static void framebuffer_update_request(VncState *vs, int incremental, - int x_position, int y_position, - int w, int h); -static void vnc_refresh(DisplayChangeListener *dcl); -static int vnc_refresh_server_surface(VncDisplay *vd); static int vnc_width(VncDisplay *vd) { @@ -1763,7 +1755,8 @@ static void check_pointer_type_change(Notifier *notifier, void *data) vs->absolute = absolute; } -static void pointer_event(VncState *vs, int button_mask, int x, int y) +static void pointer_event(VncState *vs, uint8_t button_mask, + uint16_t x, uint16_t y) { static uint32_t bmap[INPUT_BUTTON__MAX] = { [INPUT_BUTTON_LEFT] = 0x01, @@ -1841,7 +1834,7 @@ static void kbd_leds(Notifier *notifier, void *data) } } -static void do_key_event(VncState *vs, int down, int keycode, int sym) +static void do_key_event(VncState *vs, int down, int keycode, uint32_t sym) { unsigned int lnx = qemu_input_key_number_to_linux(keycode); @@ -2019,7 +2012,7 @@ static const char *code2name(int keycode) return QKeyCode_str(qemu_input_key_number_to_qcode(keycode)); } -static void key_event(VncState *vs, int down, uint32_t sym) +static void key_event(VncState *vs, bool down, uint32_t sym) { int keycode; int lsym = sym; @@ -2034,8 +2027,8 @@ static void key_event(VncState *vs, int down, uint32_t sym) do_key_event(vs, down, keycode, sym); } -static void ext_key_event(VncState *vs, int down, - uint32_t sym, uint16_t keycode) +static void ext_key_event(VncState *vs, bool down, + uint32_t sym, uint32_t keycode) { /* if the user specifies a keyboard layout, always use it */ if (keyboard_layout) { @@ -2046,8 +2039,9 @@ static void ext_key_event(VncState *vs, int down, } } -static void framebuffer_update_request(VncState *vs, int incremental, - int x, int y, int w, int h) +static void framebuffer_update_request(VncState *vs, uint8_t incremental, + uint16_t x, uint16_t y, + uint16_t w, uint16_t h) { if (incremental) { if (vs->update != VNC_STATE_UPDATE_FORCE) { @@ -2250,10 +2244,11 @@ static void send_color_map(VncState *vs) vnc_unlock_output(vs); } -static void set_pixel_format(VncState *vs, int bits_per_pixel, - int big_endian_flag, int true_color_flag, - int red_max, int green_max, int blue_max, - int red_shift, int green_shift, int blue_shift) +static void set_pixel_format(VncState *vs, uint8_t bits_per_pixel, + uint8_t big_endian_flag, uint8_t true_color_flag, + uint16_t red_max, uint16_t green_max, + uint16_t blue_max, uint8_t red_shift, + uint8_t green_shift, uint8_t blue_shift) { if (!true_color_flag) { /* Expose a reasonable default 256 color map */ @@ -2286,15 +2281,15 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, vs->client_pf.rmax = red_max ? red_max : 0xFF; vs->client_pf.rbits = ctpopl(red_max); vs->client_pf.rshift = red_shift; - vs->client_pf.rmask = red_max << red_shift; + vs->client_pf.rmask = (uint32_t)red_max << red_shift; vs->client_pf.gmax = green_max ? green_max : 0xFF; vs->client_pf.gbits = ctpopl(green_max); vs->client_pf.gshift = green_shift; - vs->client_pf.gmask = green_max << green_shift; + vs->client_pf.gmask = (uint32_t)green_max << green_shift; vs->client_pf.bmax = blue_max ? blue_max : 0xFF; vs->client_pf.bbits = ctpopl(blue_max); vs->client_pf.bshift = blue_shift; - vs->client_pf.bmask = blue_max << blue_shift; + vs->client_pf.bmask = (uint32_t)blue_max << blue_shift; vs->client_pf.bits_per_pixel = 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; From 82b4d693d522d406c950067caa4f14f45f4b679e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 6 Jul 2026 12:35:38 +0400 Subject: [PATCH 026/860] ui/input-barrier: fix off-by-one in keycode bounds check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use strict "<" to fix the off-by-one. Fixes: 6105683da35b ("ui: add an embedded Barrier client") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3951 Reported-by: huntr bubble Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Laurent Vivier Signed-off-by: Marc-Andre Lureau --- ui/input-barrier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/input-barrier.c b/ui/input-barrier.c index d07027114a..0a238d4010 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -87,7 +87,7 @@ static kbd_layout_t *kbd_layout; static unsigned int input_barrier_to_linux(uint16_t keyid, uint16_t keycode) { /* keycode is optional, if it is not provided use keyid */ - if (keycode && keycode <= qemu_input_map_xorgkbd_to_linux_len) { + if (keycode && keycode < qemu_input_map_xorgkbd_to_linux_len) { return qemu_input_map_xorgkbd_to_linux[keycode]; } From 53f0d87db7b4bc4b8c906eedcce6fec17d39824f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 28 Jun 2026 22:58:28 +0900 Subject: [PATCH 027/860] ui/gtk: Narrow DMA-BUF critical section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scanout operations need to be properly ordered to avoid tearing. The virtio specification allows the guest to use pageflip. With pageflip, the guest only modifies the invisible framebuffer while the host scans out the visible framebuffer. The guest may choose not to use pageflip to avoid its overhead, accepting the risk of tearing. ui/gtk performs the following procedure to flush a scanout: 1) Queue a draw event. 2) The draw event gets triggered. 3) Blit the guest framebuffer to the host framebuffer. When flushing a DMA-BUF scanout, ui/gtk blocks the device before 2) if possible and unblocks it after 3) to enforce proper ordering. However, blocking the device before 2) has two problems. First, it can leave the device blocked indefinitely because GTK sometimes decides to cancel 2) when the window is not visible for example. ui/gtk regularly repeats 1) as a workaround, but it is not applicable to GtkGLArea because it causes display corruption. Second, the behavior is inconsistent with the other types of scanout that leave the device unblocked between 1) and 2). To fix these problems, let ui/gtk block the device only when the queued draw event runs, immediately before 3). Blocking before that is unnecessary since ui/gtk does not access the framebuffer yet. If the guest does not use pageflip but instead updates the visible framebuffer directly, ui/gtk should not add the overhead of a pre-draw block. ui/gtk still blocks the device during 3) for DMA-BUF. Unlike the other scanout types, 3) can happen asynchronously with the device for a DMA-BUF, so ui/gtk needs to keep the visible guest framebuffer stable for the blit. With the problems fixed, the workaround to repeat 1) is no longer necessary and is removed. Signed-off-by: Akihiko Odaki Acked-by: Marc-André Lureau Message-ID: <20260628-gtk-v2-1-1e4839012f09@rsg.ci.i.u-tokyo.ac.jp> --- ui/gtk-egl.c | 6 ++---- ui/gtk-gl-area.c | 23 +---------------------- 2 files changed, 3 insertions(+), 26 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 7c5c9b2428..adc81f34b1 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -91,6 +91,7 @@ void gd_egl_draw(VirtualConsole *vc) } else { qemu_dmabuf_set_draw_submitted(dmabuf, false); } + qemu_console_hw_gl_block(vc->gfx.dcl.con, true); } #endif gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); @@ -405,14 +406,11 @@ void gd_egl_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - qemu_console_hw_gl_block(vc->gfx.dcl.con, true); qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_egl_set_scanout_mode(vc, true); - gtk_widget_queue_draw_area(area, x, y, w, h); - return; } - gd_egl_scanout_flush(&vc->gfx.dcl, x, y, w, h); + gtk_widget_queue_draw_area(area, x, y, w, h); } void gtk_egl_init(DisplayGLMode mode) diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 23806b9d01..29497019ee 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -86,6 +86,7 @@ void gd_gl_area_draw(VirtualConsole *vc) } else { qemu_dmabuf_set_draw_submitted(dmabuf, false); } + qemu_console_hw_gl_block(vc->gfx.dcl.con, true); } #endif @@ -163,27 +164,6 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); - if (vc->gfx.guest_fb.dmabuf && - qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - /* - * 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; - } - if (!vc->gfx.gls) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; @@ -347,7 +327,6 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !qemu_dmabuf_get_draw_submitted(vc->gfx.guest_fb.dmabuf)) { - qemu_console_hw_gl_block(vc->gfx.dcl.con, true); qemu_dmabuf_set_draw_submitted(vc->gfx.guest_fb.dmabuf, true); gtk_gl_area_set_scanout_mode(vc, true); } From e650e4fe0fb35b7a8ec9fc04e00346c02640bd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 9 Jul 2026 17:42:25 +0400 Subject: [PATCH 028/860] ui/vnc: fix out-of-bounds write in lossy refresh dirty marking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vnc_refresh_lossy_rect() marks a full VNC_STAT_RECT (64) rows of the dirty bitmap when refreshing a lossy tile. When the display height is not a multiple of VNC_STAT_RECT, the last tile row is a partial tile and the loop writes past the end of vs->dirty[VNC_MAX_HEIGHT]. For example, with a 2160-pixel-high display (VNC_MAX_HEIGHT), the last stat tile starts at y=2112. The unconditional 64-row loop writes rows 2112..2175, overflowing 16 rows (640 bytes) past the dirty bitmap into subsequent VncState fields. Fix by passing the effective display height into vnc_refresh_lossy_rect() and clamping the inner loop. Fixes: CVE-2026-61475 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3935 Reported-by: "Vulnerability Report" Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-Andre Lureau --- ui/vnc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 8d91cee4d9..c957731877 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3000,18 +3000,18 @@ void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h) } } -static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) +static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y, + int height) { VncState *vs; int sty = y / VNC_STAT_RECT; int stx = x / VNC_STAT_RECT; int has_dirty = 0; - int height = MIN(pixman_image_get_height(vd->guest.fb), - pixman_image_get_height(vd->server)); int rows; y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); + rows = MIN(VNC_STAT_RECT, height - y); rows = MIN(VNC_STAT_RECT, height - y); if (rows <= 0) { @@ -3083,7 +3083,7 @@ static int vnc_update_stats(VncDisplay *vd, struct timeval * tv) if (timercmp(&res, &VNC_REFRESH_LOSSY, >)) { rect->freq = 0; - has_dirty += vnc_refresh_lossy_rect(vd, x, y); + has_dirty += vnc_refresh_lossy_rect(vd, x, y, height); memset(rect->times, 0, sizeof (rect->times)); continue ; } From 6075444c5a72ab531295d92050d95cebea8a8119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 13 Jul 2026 13:24:03 +0400 Subject: [PATCH 029/860] ui/vnc: validate SetPixelFormat field ranges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VNC SetPixelFormat message carries red/green/blue_max as 16-bit values, but PixelFormat stores them as uint8_t. A client sending a max value above 255 (e.g. 0x0100) passes the existing non-zero check but silently truncates to 0 on assignment, leading to a division by zero in the Tight PNG palette path. Add explicit range checks if any channel max exceeds UINT8_MAX. Fixes: CVE-2026-15578 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3976 Reported-by: dong ling Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Signed-off-by: Marc-Andre Lureau --- ui/vnc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ui/vnc.c b/ui/vnc.c index c957731877..24aa40f730 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2271,6 +2271,11 @@ static void set_pixel_format(VncState *vs, uint8_t bits_per_pixel, return; } + if (red_max > UINT8_MAX || green_max > UINT8_MAX || blue_max > UINT8_MAX) { + vnc_client_error(vs); + return; + } + if (red_shift >= bits_per_pixel || red_shift >= 32 || green_shift >= bits_per_pixel || green_shift >= 32 || blue_shift >= bits_per_pixel || blue_shift >= 32) { From db2716bce299b35b9b8259f732f80484e4cf9870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Jul 2026 17:46:39 +0400 Subject: [PATCH 030/860] hw/display/virtio-gpu: fix dmabuf_fd leak on remap failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When virtio_gpu_create_udmabuf() succeeds but virtio_gpu_remap_udmabuf() fails (mmap returns MAP_FAILED), virtio_gpu_init_udmabuf() returns early without closing the dmabuf fd. Since res->blob is never set in this path, later cleanup via virtio_gpu_cleanup_mapping() skips virtio_gpu_fini_udmabuf() entirely, leaking the file descriptor. Call virtio_gpu_destroy_udmabuf() before the early return to close the fd. This function already handles partial state correctly: it skips the munmap when res->remapped is NULL and closes the fd when res->dmabuf_fd >= 0. Fixes: 9b60cdf98723 ("virtio-gpu: Add udmabuf helpers") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Dmitry Osipenko Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau --- hw/display/virtio-gpu-udmabuf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index d5ac1cfca0..5f08c855dd 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -143,6 +143,7 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) } virtio_gpu_remap_udmabuf(res); if (!res->remapped) { + virtio_gpu_destroy_udmabuf(res); return; } pdata = res->remapped; From 6d2cf4466f1b118350b77838217b79e2c2f0833c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 9 Jul 2026 15:15:22 +0400 Subject: [PATCH 031/860] hw/display/qxl: fix TOCTOU in cursor chunk data_size handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Snapshot chunk.data_size into a host-local variable before passing it to qxl_phys2virt() for validation, and pass it through qxl_cursor() and qxl_unpack_chunks() so that no subsequent code re-reads the field. Without this, a racing vCPU can inflate data_size between the qxl_phys2virt() validation and the memcpy in qxl_unpack_chunks(), causing a source read past the validated region. In practice the read stays within the guest's own VRAM mmap, so the impact is limited. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3757 Reviewed-by: Philippe Mathieu-Daudé Reported-by: Feifan Qian Signed-off-by: Marc-Andre Lureau --- hw/display/qxl-render.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 3bf634ee05..4799c9e8be 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -217,7 +217,8 @@ void qxl_render_update_area_done(PCIQXLDevice *qxl, QXLCookie *cookie) } static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, - QXLDataChunk *chunk, uint32_t group_id) + QXLDataChunk *chunk, uint32_t group_id, + uint32_t chunk_data_size) { uint32_t max_chunks = 32; size_t offset = 0; @@ -225,22 +226,21 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, QXLPHYSICAL next_chunk_phys = 0; for (;;) { - bytes = MIN(size - offset, chunk->data_size); + bytes = MIN(size - offset, chunk_data_size); memcpy(dest + offset, chunk->data, bytes); offset += bytes; if (offset == size) { return; } 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_data_size = chunk->data_size; chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, - sizeof(QXLDataChunk) + chunk->data_size); + sizeof(QXLDataChunk) + chunk_data_size); if (!chunk) { return; } @@ -252,7 +252,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, } static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, - uint32_t group_id) + uint32_t group_id, uint32_t chunk_data_size) { QEMUCursor *c; uint8_t *and_mask, *xor_mask; @@ -272,11 +272,11 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, case SPICE_CURSOR_TYPE_MONO: /* Assume that the full cursor is available in a single chunk. */ size = 2 * cursor_get_mono_bpl(c) * c->height; - if (size != cursor->data_size || cursor->chunk.data_size < size) { + if (size != cursor->data_size || chunk_data_size < size) { qxl_set_guest_bug(qxl, "%s: bad monochrome cursor %ux%u" " data_size %u chunk_size %u", __func__, c->width, c->height, - cursor->data_size, cursor->chunk.data_size); + cursor->data_size, chunk_data_size); goto fail; } and_mask = cursor->chunk.data; @@ -288,7 +288,8 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, break; case SPICE_CURSOR_TYPE_ALPHA: size = sizeof(uint32_t) * c->width * c->height; - qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id); + qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id, + chunk_data_size); if (qxl->debug > 2) { cursor_print_ascii_art(c, "qxl/alpha"); } @@ -325,19 +326,23 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) } switch (cmd->type) { case QXL_CURSOR_SET: + { + uint32_t chunk_data_size; + /* First read the QXLCursor to get QXLDataChunk::data_size ... */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, sizeof(QXLCursor)); if (!cursor) { return 1; } + chunk_data_size = cursor->chunk.data_size; /* Then read including the chunked data following QXLCursor. */ cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id, - sizeof(QXLCursor) + cursor->chunk.data_size); + sizeof(QXLCursor) + chunk_data_size); if (!cursor) { return 1; } - c = qxl_cursor(qxl, cursor, ext->group_id); + c = qxl_cursor(qxl, cursor, ext->group_id, chunk_data_size); if (c == NULL) { c = cursor_builtin_left_ptr(); } @@ -351,6 +356,7 @@ int qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) qemu_mutex_unlock(&qxl->ssd.lock); qemu_bh_schedule(qxl->ssd.cursor_bh); break; + } case QXL_CURSOR_MOVE: qemu_mutex_lock(&qxl->ssd.lock); qxl->ssd.mouse_x = cmd->u.position.x; From 861372428b05f74a1cf9a8af22a863aa7b46c7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 15 Jul 2026 11:55:46 +0400 Subject: [PATCH 032/860] hw/display/virtio-gpu: validate stride against width on scanout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validate that the framebuffer stride is at least width * bytes_per_pixel in both virtio_gpu_scanout_blob_to_fb() and virtio_gpu_do_set_scanout(). A guest can set a very small stride while using a large width. The total size check (offset + stride * height <= blob_size) passes because stride * height is small, but pixman reads width * bytes_per_pixel per row, causing heap OOB reads. The leaked data is rendered to the host display. The check is added in virtio_gpu_do_set_scanout() to cover all paths: blob scanout, non-blob scanout and migration post_load. The additional early check in virtio_gpu_scanout_blob_to_fb() rejects invalid blob configurations early. Fixes: CVE-2026-63109 Fixes: 7b5574225429 ("hw/display: check frame buffer can hold blob") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3989 Reported-by: Tristan @TristanInSec Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau --- hw/display/virtio-gpu.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 88526051a9..e00fb6effa 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -646,6 +646,14 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, return false; } + if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: stride %u too small for width %u at %u bpp\n", + __func__, fb->stride, fb->width, fb->bytes_pp); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + g->parent_obj.enable = 1; if (res->blob) { @@ -753,6 +761,14 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, fb->width = ss->width; fb->height = ss->height; fb->stride = ss->strides[0]; + + if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: stride %u too small for width %u at %u bpp\n", + __func__, fb->stride, fb->width, fb->bytes_pp); + return false; + } + fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * fb->stride; fbend = fb->offset; From 8720061c6d343c77b4f3a061cce631c05ecc0482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Jul 2026 12:34:02 +0400 Subject: [PATCH 033/860] hw/display/virtio-gpu: cap submit_3d command buffer allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both virgl_cmd_submit_3d() and rutabaga_cmd_submit_3d() pass the guest-controlled cs.size directly to malloc() without bounds checking. A malicious guest can set cs.size to an arbitrarily large value, causing an OOM abort that crashes the QEMU process. Checking cs.size against the descriptor payload length (iov_size) is not sufficient: indirect descriptor tables can repeat entries aliasing the same guest-physical range, inflating iov_size() to nearly 4 GiB while referring to only a small amount of unique memory. Instead, cap cs.size at 4 MiB. With 4 KiB pages and QEMU's VIRTQUEUE_MAX_SIZE (1024) mapped-iov limit, the Linux virtio driver cannot carry more than ~4 MiB of inline command data, so legitimate submissions are unaffected. Fixes: 9d9e152136bd ("virtio-gpu: add 3d mode and virgl rendering support.") Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream") Fixes: d52c454aadc ("contrib: add vhost-user-gpu") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3776 Reported-by: admin@fluentlogic.org Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau --- contrib/vhost-user-gpu/virgl.c | 7 +++++++ contrib/vhost-user-gpu/vugpu.h | 9 +++++++++ hw/display/virtio-gpu-rutabaga.c | 8 ++++++++ hw/display/virtio-gpu-virgl.c | 8 ++++++++ include/hw/virtio/virtio-gpu.h | 9 +++++++++ 5 files changed, 41 insertions(+) diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index 51da0e3667..550fd03bf5 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -202,6 +202,13 @@ virgl_cmd_submit_3d(VuGpu *g, VUGPU_FILL_CMD(cs); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + g_critical("%s: command buffer too large (%u)", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + buf = g_malloc(cs.size); s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index 654c392fbb..2374eb90cb 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -22,6 +22,7 @@ #include "qemu/queue.h" #include "qemu/iov.h" #include "qemu/bswap.h" +#include "qemu/units.h" #include "vugbm.h" typedef enum VhostUserGpuRequest { @@ -163,6 +164,14 @@ struct virtio_gpu_ctrl_command { QTAILQ_ENTRY(virtio_gpu_ctrl_command) next; }; +/* + * With 4 KiB pages and QEMU's VIRTQUEUE_MAX_SIZE (1024) mapped-iov + * limit, the largest inline command is ~4 MiB. Cap submit_3d + * allocations to this value to prevent a malicious guest from + * triggering an OOM abort via an inflated cs.size field. + */ +#define VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE (4 * MiB) + #define VUGPU_FILL_CMD(out) do { \ size_t vugpufillcmd_s_ = \ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index 6ff1263901..4d7d7b2459 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -351,6 +351,14 @@ rutabaga_cmd_submit_3d(VirtIOGPU *g, VIRTIO_GPU_FILL_CMD(cs); trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: command buffer too large (%u)\n", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + buf = g_new0(uint8_t, cs.size); s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index 60c78af06a..d9e5b01104 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -607,6 +607,14 @@ static void virgl_cmd_submit_3d(VirtIOGPU *g, VIRTIO_GPU_FILL_CMD(cs); trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size); + if (cs.size > VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: command buffer too large (%u)\n", + __func__, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + buf = g_malloc(cs.size); s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index f69fc19462..b9bad27c97 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -15,6 +15,7 @@ #define HW_VIRTIO_GPU_H #include "qemu/queue.h" +#include "qemu/units.h" #include "ui/qemu-pixman.h" #include "ui/console.h" #include "hw/virtio/virtio.h" @@ -299,6 +300,14 @@ struct VirtIOGPURutabaga { struct rutabaga *rutabaga; }; +/* + * With 4 KiB pages and QEMU's VIRTQUEUE_MAX_SIZE (1024) mapped-iov + * limit, the largest inline command is ~4 MiB. Cap submit_3d + * allocations to this value to prevent a malicious guest from + * triggering an OOM abort via an inflated cs.size field. + */ +#define VIRTIO_GPU_MAX_CMD_SUBMIT_SIZE (4 * MiB) + #define VIRTIO_GPU_FILL_CMD(out) do { \ size_t virtiogpufillcmd_s_ = \ iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, 0, \ From e99488c5d4a009a156a29f9ff6a7f12d091c2646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Jul 2026 16:53:33 +0400 Subject: [PATCH 034/860] hw/display/virtio-gpu: handle migration iov allocation failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An unbounded iov_cnt from the migration stream drives two g_new() allocations whose combined size can exceed available memory, causing GLib to abort the process. Switch to g_try_new() and propagate the failure as a migration error. Fixes: 0c244e50ee12 ("virtio-gpu: add live migration support") Fixes: f66767f75c9c ("virtio-gpu: add virtio-gpu/blob vmstate subsection") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3753 Reported-by: Feifan Qian Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau --- hw/display/virtio-gpu.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index e00fb6effa..048b151872 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1365,8 +1365,15 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } - res->addrs = g_new(uint64_t, res->iov_cnt); - res->iov = g_new(struct iovec, res->iov_cnt); + res->addrs = g_try_new(uint64_t, res->iov_cnt); + res->iov = g_try_new(struct iovec, res->iov_cnt); + if (res->iov_cnt && (!res->addrs || !res->iov)) { + pixman_image_unref(res->image); + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } /* read data */ for (i = 0; i < res->iov_cnt; i++) { @@ -1440,8 +1447,15 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, res->resource_id = resource_id; res->blob_size = qemu_get_be32(f); res->iov_cnt = qemu_get_be32(f); - res->addrs = g_new(uint64_t, res->iov_cnt); - res->iov = g_new(struct iovec, res->iov_cnt); + + res->addrs = g_try_new(uint64_t, res->iov_cnt); + res->iov = g_try_new(struct iovec, res->iov_cnt); + if (res->iov_cnt && (!res->addrs || !res->iov)) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } /* read data */ for (i = 0; i < res->iov_cnt; i++) { From 9c724426f5192845b5975dc48165de06a3da50b2 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 17 Jul 2026 16:41:36 +0900 Subject: [PATCH 035/860] hw/display/virtio-gpu: reject strides exceeding INT_MAX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VIRTIO_GPU_CMD_SET_SCANOUT_BLOB supplies a guest-controlled uint32_t stride, but some downstream consumers take it as int. They may interpret a value greater than INT_MAX as negative and cause issues: - pixman_image_create_bits() takes the stride as int, and Pixman may later access memory before the blob buffer. - eglCreateImageKHR() also takes the stride as EGLint when importing the DMA-BUF, and Mesa rejects it. Reject such strides before scanout. The check in virtio_gpu_scanout_blob_to_fb() rejects unsupported blob configurations early. The check added in virtio_gpu_do_set_scanout() covers migration post_load. Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260717-int-v1-1-8aa05e1791a0@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 048b151872..3090edf89a 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -654,6 +654,14 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, return false; } + if (fb->stride > INT_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: stride is %" PRIu32 + ", larger than the supported maximum (%d)\n", + __func__, fb->stride, INT_MAX); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + g->parent_obj.enable = 1; if (res->blob) { @@ -769,6 +777,13 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, return false; } + if (fb->stride > INT_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: stride is %" PRIu32 + ", larger than the supported maximum (%d)\n", + __func__, fb->stride, INT_MAX); + return false; + } + fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * fb->stride; fbend = fb->offset; From f1af81ba2aeec7b5a5ee8529ef55275865aea0c0 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 17 Jul 2026 16:05:23 +0900 Subject: [PATCH 036/860] hw/display/virtio-gpu: Check pixman_image_create_bits() results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pixman_image_create_bits() returns NULL for allocation failures and unaligned strides, which may be supplied by the guest with VIRTIO_GPU_CMD_SET_SCANOUT_BLOB, and virtio_gpu_do_set_scanout() subsequently dereferences it. Fixes: fa06e5cb7b10 ("virtio-gpu: fix scanout rectangles") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260717-pixman-v1-1-89ea33b50b75@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 3090edf89a..718ba30392 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -690,6 +690,10 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, void *ptr = data + fb->offset; rect = pixman_image_create_bits(fb->format, r->width, r->height, ptr, fb->stride); + if (!rect) { + *error = VIRTIO_GPU_RESP_ERR_UNSPEC; + return false; + } if (res->image) { pixman_image_ref(res->image); From c923ae77e70abda40d786e7a367517a61969235b Mon Sep 17 00:00:00 2001 From: Bin Guo Date: Thu, 16 Jul 2026 18:19:50 +0800 Subject: [PATCH 037/860] migration: fix ineffective overflow assert in postcopy blocktime vcpu_faults_current[] is uint8_t. The overflow assert was checked after the post-increment, so 255 would wrap to 0 and the assert would pass silently. Move the check before the increment and use < 255. Signed-off-by: Bin Guo Link: https://lore.kernel.org/r/20260716101952.65329-2-guobin@linux.alibaba.com Signed-off-by: Peter Xu --- migration/postcopy-ram.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 980b938a4c..b0828edb3e 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -1093,7 +1093,11 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, /* * Account how many concurrent faults on this vCPU we trapped. See * comments above vcpu_faults_current[] on why it can be more than one. + * + * vcpu_faults_current[] is uint8_t, so assert before incrementing to + * catch overflow before it wraps. */ + assert(dc->vcpu_faults_current[cpu] < 255); if (dc->vcpu_faults_current[cpu]++ == 0) { dc->smp_cpus_down++; /* @@ -1103,9 +1107,6 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, */ dc->last_begin = current; } - - /* Making sure it won't overflow - it really should never! */ - assert(dc->vcpu_faults_current[cpu] <= 255); } else { /* * For non-vCPU thread faults, we don't care about tid or cpu index From 60f2281450c4b4e9a16922fd69c66486a8207abb Mon Sep 17 00:00:00 2001 From: Bin Guo Date: Thu, 16 Jul 2026 18:19:52 +0800 Subject: [PATCH 038/860] migration: clean up postcopy blocktime presentation Small cleanups for the postcopy blocktime fine-grained tracking feature: * Remove a redundant memset() on latency_buckets after g_new0(). * Use singular "Latency" in the HMP label for postcopy non-vCPU latency, consistent with other single-value labels. Signed-off-by: Bin Guo Link: https://lore.kernel.org/r/20260716101952.65329-4-guobin@linux.alibaba.com Signed-off-by: Peter Xu --- migration/migration-hmp-cmds.c | 2 +- migration/postcopy-ram.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 8b385f560e..7ec010fd05 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -95,7 +95,7 @@ static void migration_dump_blocktime(Monitor *mon, MigrationInfo *info) } if (info->has_postcopy_non_vcpu_latency) { - monitor_printf(mon, "Postcopy non-vCPU Latencies (ns): %" PRIu64 "\n", + monitor_printf(mon, "Postcopy non-vCPU Latency (ns): %" PRIu64 "\n", info->postcopy_non_vcpu_latency); } diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index b0828edb3e..a3314d3180 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -297,9 +297,6 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) unsigned int smp_cpus = ms->smp.cpus; PostcopyBlocktimeContext *ctx = g_new0(PostcopyBlocktimeContext, 1); - /* Initialize all counters to be zeros */ - memset(ctx->latency_buckets, 0, sizeof(ctx->latency_buckets)); - ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_count = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_current = g_new0(uint8_t, smp_cpus); From 390bf4fbc311297cb91f620937d8c566ab35c8db Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Sat, 11 Jul 2026 11:54:33 -0700 Subject: [PATCH 039/860] tests/docker: hexagon fix podman untar, 22.1.8 The debian-hexagon-cross image previously unpacked the codelinaro clang+llvm hexagon toolchain by piping a .tar.zst archive straight into "tar --zstd -xC". GNU tar restores the archive's stored file modes on extraction, including lchmod() on symlinks. Under rootless podman, where the build runs in a user namespace on overlay storage, those chmod()/lchmod() calls are rejected with EPERM: tar: .../libclang_rt.builtins.a: Cannot change mode to rwxrwxrwx: \ Operation not permitted tar: .../x86_64-linux-gnu: Cannot change mode to rwxr-xr-x: \ Operation not permitted tar: Exiting with failure status due to previous errors tar then exits non-zero and aborts the build, so debian-hexagon-cross only builds reliably under docker, not rootless podman. So instead we can switch to a different packaging. clang, lld come from LLVM Debian builds from apt.llvm.org, instead of custom ones for hexagon. And only the hexagon linux sysroot is required, avoiding the tar issues. Reviewed-by: Matheus Tavares Bernardino Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- .../dockerfiles/debian-hexagon-cross.docker | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tests/docker/dockerfiles/debian-hexagon-cross.docker b/tests/docker/dockerfiles/debian-hexagon-cross.docker index 23e8bb2fb2..e07a2be37e 100644 --- a/tests/docker/dockerfiles/debian-hexagon-cross.docker +++ b/tests/docker/dockerfiles/debian-hexagon-cross.docker @@ -17,13 +17,13 @@ RUN apt-get update && \ # Install common build utilities apt-get install -y --no-install-recommends \ curl \ + wget \ + gnupg \ + lsb-release \ ccache \ xz-utils \ zstd \ ca-certificates \ - libc++1 \ - libc++abi1 \ - libunwind-19 \ bison \ flex \ git \ @@ -43,15 +43,22 @@ RUN apt-get update && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt -ENV TOOLCHAIN_INSTALL=/opt -ENV TOOLCHAIN_RELEASE=22.1.0 -ENV TOOLCHAIN_BASENAME=clang+llvm-${TOOLCHAIN_RELEASE}-cross-hexagon-unknown-linux-musl -ENV TOOLCHAIN_URL=https://artifacts.codelinaro.org/artifactory/codelinaro-toolchain-for-hexagon/${TOOLCHAIN_RELEASE}_/${TOOLCHAIN_BASENAME}.tar.zst +ENV LLVM_VERSION=22 +ENV TOOLCHAIN_RELEASE=22.1.8 +ENV TOOLCHAIN_URL=https://artifacts.codelinaro.org/artifactory/codelinaro-toolchain-for-hexagon/${TOOLCHAIN_RELEASE}________/hexagon-debs-${TOOLCHAIN_RELEASE}________.tar.gz ENV CCACHE_WRAPPERSDIR=/usr/libexec/ccache-wrappers - -RUN curl -#SL "$TOOLCHAIN_URL" | tar --zstd -xC "$TOOLCHAIN_INSTALL" -ENV PATH=$PATH:${TOOLCHAIN_INSTALL}/${TOOLCHAIN_BASENAME}/x86_64-linux-gnu/bin ENV MAKE=/usr/bin/make + +RUN curl -#SL https://apt.llvm.org/llvm.sh -o /tmp/llvm.sh && \ + chmod +x /tmp/llvm.sh && \ + DEBIAN_FRONTEND=noninteractive eatmydata /tmp/llvm.sh ${LLVM_VERSION} && \ + rm -f /tmp/llvm.sh && \ + mkdir -p /tmp/hexagon-debs && \ + curl -#SL "$TOOLCHAIN_URL" | tar -xzC /tmp/hexagon-debs && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y /tmp/hexagon-debs/*.deb && \ + rm -rf /tmp/hexagon-debs + # As a final step configure the user (if env is defined) ARG USER ARG UID From e872e09add94425b634552e323f2e8c32fac5fba Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 16 Jul 2026 21:19:16 -0700 Subject: [PATCH 040/860] gitlab-ci: build hexagon-softmmu to enable functional test Add hexagon-softmmu to build-system-debian's target list so that the existing functional-system-debian job picks up hexagon functional test. Reviewed-by: Thomas Huth Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- .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 e2553e7326..831d9a2674 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -70,8 +70,8 @@ build-system-debian: variables: IMAGE: debian CONFIGURE_ARGS: --with-coroutine=sigaltstack --enable-rust - TARGETS: arm-softmmu i386-softmmu riscv64-softmmu sh4eb-softmmu - sparc-softmmu xtensa-softmmu + TARGETS: arm-softmmu hexagon-softmmu i386-softmmu riscv64-softmmu + sh4eb-softmmu sparc-softmmu xtensa-softmmu MAKE_CHECK_ARGS: check-build check-system-debian: From 8aea63e9650df7ece512be227b01f648ab52e069 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 16 Jul 2026 11:41:32 -0700 Subject: [PATCH 041/860] target/hexagon: fix -Wshadow=local build error on GCC 14 With --disable-hexagon-idef-parser, tcg_funcs_generated.c.inc fails to build under GCC 14 with: error: declaration of 'HALF' shadows a previous local [-Werror=shadow=local] This became a hard error with GCC ced651b7757e `c23: tag compatibility rules for enums`. After that GCC change, DECL_FILE_SCOPE_P is false for all enumerators, so shadowing now falls under -Wshadow=local. Rename the local variable to tmp_half to avoid the collision. Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- target/hexagon/gen_tcg.h | 4 ++-- target/hexagon/macros.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/hexagon/gen_tcg.h b/target/hexagon/gen_tcg.h index a28af0c245..40e03781d3 100644 --- a/target/hexagon/gen_tcg.h +++ b/target/hexagon/gen_tcg.h @@ -414,7 +414,7 @@ #define fGEN_TCG_STORE(SHORTCODE) \ do { \ - TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \ + TCGv tmp_half G_GNUC_UNUSED = tcg_temp_new(); \ TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \ SHORTCODE; \ } while (0) @@ -422,7 +422,7 @@ #define fGEN_TCG_STORE_pcr(SHIFT, STORE) \ do { \ TCGv ireg = tcg_temp_new(); \ - TCGv HALF G_GNUC_UNUSED = tcg_temp_new(); \ + TCGv tmp_half G_GNUC_UNUSED = tcg_temp_new(); \ TCGv BYTE G_GNUC_UNUSED = tcg_temp_new(); \ tcg_gen_mov_tl(EA, RxV); \ gen_read_ireg(ireg, MuV, SHIFT); \ diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 21ab8ae5bb..8a32d5f79c 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -576,8 +576,8 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) } while (0) #ifdef QEMU_GENERATE -#define fGETHALF(N, SRC) gen_get_half(HALF, N, SRC, true) -#define fGETUHALF(N, SRC) gen_get_half(HALF, N, SRC, false) +#define fGETHALF(N, SRC) gen_get_half(tmp_half, N, SRC, true) +#define fGETUHALF(N, SRC) gen_get_half(tmp_half, N, SRC, false) #else #define fGETHALF(N, SRC) ((int16_t)((SRC >> ((N) * 16)) & 0xffff)) #define fGETUHALF(N, SRC) ((uint16_t)((SRC >> ((N) * 16)) & 0xffff)) From d7a6df755b97c959925cad5439e2c688d08b268f Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Jul 2026 18:09:51 -0700 Subject: [PATCH 042/860] target/hexagon: handle misaligned load/store cause codes in cpu_loop Add a linux-user cpu_loop handler for HEX_CAUSE_MISALIGNED_LOAD and HEX_CAUSE_MISALIGNED_STORE, raising SIGBUS for consistency with hexswi.c's sysemu handling, even though linux-user's own alignment check in accel/tcg/user-exec.c currently raises SIGBUS before Hexagon's cause-code machinery is reached. Reviewed-by: Pierrick Bouvier Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- linux-user/hexagon/cpu_loop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 0958c51fbb..d7f73439db 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -76,6 +76,11 @@ void cpu_loop(CPUHexagonState *env) force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->gpr[HEX_REG_PC]); break; + case HEX_CAUSE_MISALIGNED_LOAD: + case HEX_CAUSE_MISALIGNED_STORE: + force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, + env->gpr[HEX_REG_PC]); + break; default: EXCP_DUMP(env, "\nqemu: unhandled CPU precise exception " "cause code 0x%x - aborting\n", From 93cf73051435c8695d7d0b04dbad4bb8d5339dd2 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Jul 2026 18:09:52 -0700 Subject: [PATCH 043/860] target/hexagon: raise SIGBUS on unaligned scalar loads hexagon-linux-user had no enforcement of alignment for scalar load exceptions, and unaligned loads silently succeeded instead of raising SIGBUS. Add MO_ALIGN to the MEM_LOAD* macros, the locked-load and load-frame helpers in genptr.c, and the idef-parser's fLOAD. Reviewed-by: Pierrick Bouvier Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- target/hexagon/genptr.c | 6 ++--- target/hexagon/idef-parser/parser-helpers.c | 2 +- target/hexagon/macros.h | 26 +++++++++++++-------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 1ebc747140..05c9aa0446 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -470,14 +470,14 @@ void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src) static inline void gen_load_locked4u(TCGv dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_LE | MO_UL); + tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_LE | MO_UL | MO_ALIGN); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_tl(hex_llsc_val, dest); } static inline void gen_load_locked8u(TCGv_i64 dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_LE | MO_UQ); + tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_LE | MO_UQ | MO_ALIGN); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_i64(hex_llsc_val_i64, dest); } @@ -890,7 +890,7 @@ static void gen_load_frame(DisasContext *ctx, TCGv_i64 frame, TCGv EA) { Insn *insn = ctx->insn; /* Needed for CHECK_NOSHUF */ CHECK_NOSHUF(EA, 8); - tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ); + tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ | MO_ALIGN); } /* Stack overflow check */ diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index b942d9ea16..49cc11fddd 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1770,7 +1770,7 @@ void gen_load(Context *c, YYLTYPE *locp, HexValue *width, if (signedness == SIGNED) { OUT(c, locp, " | MO_SIGN"); } - OUT(c, locp, " | MO_LE);\n"); + OUT(c, locp, " | MO_LE | MO_ALIGN);\n"); } void gen_store(Context *c, YYLTYPE *locp, HexValue *width, HexValue *ea, diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 8a32d5f79c..a5b9c24c6b 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -106,37 +106,37 @@ #define MEM_LOAD1s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 1); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_SB); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_SB | MO_ALIGN); \ } while (0) #define MEM_LOAD1u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 1); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_UB); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_UB | MO_ALIGN); \ } while (0) #define MEM_LOAD2s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SW | MO_ALIGN); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UW | MO_ALIGN); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SL | MO_ALIGN); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UL | MO_ALIGN); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 8); \ - tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_LE | MO_UQ); \ + tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_LE | MO_UQ | MO_ALIGN); \ } while (0) #define MEM_STORE1_FUNC(X) \ @@ -519,9 +519,15 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val, int shift) #define fLOAD(NUM, SIZE, SIGN, EA, DST) MEM_LOAD##SIZE##SIGN(DST, EA) #else #define MEM_LOAD1 cpu_ldub_data_ra -#define MEM_LOAD2 cpu_lduw_le_data_ra -#define MEM_LOAD4 cpu_ldl_le_data_ra -#define MEM_LOAD8 cpu_ldq_le_data_ra +#define MEM_LOAD2(ENV, EA, RA) \ + cpu_ldw_mmu(ENV, EA, make_memop_idx(MO_LEUW | MO_ALIGN, \ + cpu_mmu_index(env_cpu(ENV), false)), RA) +#define MEM_LOAD4(ENV, EA, RA) \ + cpu_ldl_mmu(ENV, EA, make_memop_idx(MO_LEUL | MO_ALIGN, \ + cpu_mmu_index(env_cpu(ENV), false)), RA) +#define MEM_LOAD8(ENV, EA, RA) \ + cpu_ldq_mmu(ENV, EA, make_memop_idx(MO_LEUQ | MO_ALIGN, \ + cpu_mmu_index(env_cpu(ENV), false)), RA) #define fLOAD(NUM, SIZE, SIGN, EA, DST) \ do { \ From 84b9a537d6d3c7d336d437701c28d9545f8c19fa Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Jul 2026 18:09:53 -0700 Subject: [PATCH 044/860] target/hexagon: raise SIGBUS on unaligned scalar stores hexagon-linux-user had no enforcement of alignment for scalar store exceptions, and unaligned stores silently succeeded instead of raising SIGBUS. Stores are logged rather than committed immediately, so both commit paths need the fix. Reviewed-by: Pierrick Bouvier Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- target/hexagon/genptr.c | 4 ++-- target/hexagon/op_helper.c | 13 ++++++++++--- target/hexagon/translate.c | 6 +++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 05c9aa0446..f48a27bc09 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -495,7 +495,7 @@ static inline void gen_store_conditional4(DisasContext *ctx, zero = tcg_constant_tl(0); tmp = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(tmp, hex_llsc_addr, hex_llsc_val, src, - ctx->mem_idx, MO_32); + ctx->mem_idx, MO_32 | MO_ALIGN); tcg_gen_movcond_tl(TCG_COND_EQ, pred, tmp, hex_llsc_val, one, zero); tcg_gen_br(done); @@ -520,7 +520,7 @@ static inline void gen_store_conditional8(DisasContext *ctx, zero = tcg_constant_i64(0); tmp = tcg_temp_new_i64(); tcg_gen_atomic_cmpxchg_i64(tmp, hex_llsc_addr, hex_llsc_val_i64, src, - ctx->mem_idx, MO_64); + ctx->mem_idx, MO_64 | MO_ALIGN); tcg_gen_movcond_i64(TCG_COND_EQ, tmp, tmp, hex_llsc_val_i64, one, zero); tcg_gen_extrl_i64_i32(pred, tmp); diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 125952aee5..3ce223caba 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -95,19 +95,26 @@ static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra) { uint32_t width = env->mem_log_stores[slot_num].width; target_ulong va = env->mem_log_stores[slot_num].va; + MemOpIdx oi; switch (width) { case 1: cpu_stb_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); break; case 2: - cpu_stw_le_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); + oi = make_memop_idx(MO_LEUW | MO_ALIGN, + cpu_mmu_index(env_cpu(env), false)); + cpu_stw_mmu(env, va, env->mem_log_stores[slot_num].data32, oi, ra); break; case 4: - cpu_stl_le_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra); + oi = make_memop_idx(MO_LEUL | MO_ALIGN, + cpu_mmu_index(env_cpu(env), false)); + cpu_stl_mmu(env, va, env->mem_log_stores[slot_num].data32, oi, ra); break; case 8: - cpu_stq_le_data_ra(env, va, env->mem_log_stores[slot_num].data64, ra); + oi = make_memop_idx(MO_LEUQ | MO_ALIGN, + cpu_mmu_index(env_cpu(env), false)); + cpu_stq_mmu(env, va, env->mem_log_stores[slot_num].data64, oi, ra); break; default: g_assert_not_reached(); diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 77235916f4..5d54ce8caa 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -896,17 +896,17 @@ void process_store(DisasContext *ctx, int slot_num) case 2: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_LE | MO_UW); + ctx->mem_idx, MO_LE | MO_UW | MO_ALIGN); break; case 4: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_LE | MO_UL); + ctx->mem_idx, MO_LE | MO_UL | MO_ALIGN); break; case 8: tcg_gen_qemu_st_i64(hex_store_val64[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_LE | MO_UQ); + ctx->mem_idx, MO_LE | MO_UQ | MO_ALIGN); break; default: { From 13eb268182476414c4604302404a08ee09b8572e Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Jul 2026 18:09:54 -0700 Subject: [PATCH 045/860] target/hexagon: raise SIGBUS on unaligned data access in sysemu hexagon-softmmu had no enforcement of alignment for scalar loads and stores: the MO_ALIGN flag added by the previous two commits triggers TLB_INVALID_MASK/alignment faults in cputlb.c, but Hexagon's TCGCPUOps did not implement do_unaligned_access, so the fault was never delivered to the guest. Add hexagon_cpu_do_unaligned_access(), which raises HEX_CAUSE_MISALIGNED_LOAD/_STORE via the existing HEX_EVENT_PRECISE path, mirroring raise_tlbmiss_exception()/raise_perm_exception(). Reviewed-by: Pierrick Bouvier Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- target/hexagon/cpu.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 42d93e5da4..e2af736a99 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -637,6 +637,18 @@ static void raise_perm_exception(CPUState *cs, uint32_t VA, int slot, cs->exception_index = excp; } +static void raise_misaligned_exception(CPUState *cs, uint32_t VA, int slot, + MMUAccessType access_type) +{ + CPUHexagonState *env = cpu_env(cs); + int32_t excp = (access_type == MMU_DATA_STORE) ? + HEX_CAUSE_MISALIGNED_STORE : HEX_CAUSE_MISALIGNED_LOAD; + + set_badva_regs(env, VA, slot, access_type); + cs->exception_index = HEX_EVENT_PRECISE; + env->cause_code = excp; +} + static const char *access_type_names[] = { "MMU_DATA_LOAD ", "MMU_DATA_STORE", "MMU_INST_FETCH" }; @@ -715,6 +727,18 @@ static vaddr hexagon_pointer_wrap(CPUState *cs, int mmu_idx, return result; } +static G_NORETURN +void hexagon_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) +{ + CPUHexagonState *env = cpu_env(cs); + + raise_misaligned_exception(cs, addr, 0, access_type); + do_raise_exception(env, cs->exception_index, env->gpr[HEX_REG_PC], + retaddr); +} + #endif static const TCGCPUOps hexagon_tcg_ops = { @@ -732,6 +756,7 @@ static const TCGCPUOps hexagon_tcg_ops = { .pointer_wrap = hexagon_pointer_wrap, .cpu_exec_reset = cpu_reset, .tlb_fill = hexagon_tlb_fill, + .do_unaligned_access = hexagon_cpu_do_unaligned_access, .cpu_exec_halt = hexagon_cpu_has_work, .do_interrupt = hexagon_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ From 430261acacacad33c6a230fd5f7aa526905ae9db Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Jul 2026 18:09:55 -0700 Subject: [PATCH 046/860] tests/tcg/hexagon: add unaligned scalar test Add unaligned_data.c to exercise unaligned memh/memw/memd accesses and verify SIGBUS is raised and caught. Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- tests/tcg/hexagon/Makefile.target | 2 + tests/tcg/hexagon/unaligned_data.c | 118 +++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 tests/tcg/hexagon/unaligned_data.c diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 70a8ec2e7f..a2a0ffc69b 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -54,6 +54,7 @@ HEX_TESTS += invalid-slots HEX_TESTS += invalid-encoding HEX_TESTS += multiple-writes HEX_TESTS += unaligned_pc +HEX_TESTS += unaligned_data HEX_TESTS += test_abs HEX_TESTS += test_bitcnt @@ -108,6 +109,7 @@ preg_alias: preg_alias.c hex_test.h read_write_overlap: read_write_overlap.c hex_test.h reg_mut: reg_mut.c hex_test.h test_pnew_jump_loads: test_pnew_jump_loads.c hex_test.h +unaligned_data: unaligned_data.c hex_test.h unaligned_pc: unaligned_pc.c # Compile for v66 so that the ELF selects a v66 CPU; the test then diff --git a/tests/tcg/hexagon/unaligned_data.c b/tests/tcg/hexagon/unaligned_data.c new file mode 100644 index 0000000000..e0375faafb --- /dev/null +++ b/tests/tcg/hexagon/unaligned_data.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * Test that unaligned scalar loads raise SIGBUS. + */ + +#include +#include +#include +#include +#include +#include + +int err; + +#include "hex_test.h" + +static bool sigbus_caught; +static sigjmp_buf jmp_env; +static uint64_t buf[2] = { 0, 0 }; + +static void sigbus_handler(int sig, siginfo_t *info, void *puc) +{ + check32(sig, SIGBUS); + sigbus_caught = true; + siglongjmp(jmp_env, 1); +} + +static void test_unaligned_load(int size, int offset) +{ + char *p = (char *)buf + offset; + uint32_t dummy32; + uint64_t dummy64; + + sigbus_caught = false; + if (sigsetjmp(jmp_env, 1) == 0) { + switch (size) { + case 2: + asm volatile("%[dst] = memh(%[src])\n\t" + : [dst] "=r"(dummy32) : [src] "r"(p) : "memory"); + break; + case 4: + asm volatile("%[dst] = memw(%[src])\n\t" + : [dst] "=r"(dummy32) : [src] "r"(p) : "memory"); + break; + case 8: + asm volatile("%[dst] = memd(%[src])\n\t" + : [dst] "=r"(dummy64) : [src] "r"(p) : "memory"); + break; + default: + abort(); + } + } + check32(sigbus_caught, true); +} + +static void test_unaligned_store(int size, int offset) +{ + char *p = (char *)buf + offset; + uint32_t val32 = 0x11223344; + uint64_t val64 = 0x1122334455667788ULL; + + sigbus_caught = false; + if (sigsetjmp(jmp_env, 1) == 0) { + switch (size) { + case 2: + asm volatile("memh(%[addr]) = %[val]\n\t" + : : [addr] "r"(p), [val] "r"(val32) : "memory"); + break; + case 4: + asm volatile("memw(%[addr]) = %[val]\n\t" + : : [addr] "r"(p), [val] "r"(val32) : "memory"); + break; + case 8: + asm volatile("memd(%[addr]) = %[val]\n\t" + : : [addr] "r"(p), [val] "r"(val64) : "memory"); + break; + default: + abort(); + } + } + check32(sigbus_caught, true); +} + +int main() +{ + struct sigaction act; + + act.sa_sigaction = sigbus_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + chk_error(sigaction(SIGBUS, &act, NULL)); + + test_unaligned_load(2, 1); + test_unaligned_load(4, 1); + test_unaligned_load(4, 2); + test_unaligned_load(4, 3); + test_unaligned_load(8, 1); + test_unaligned_load(8, 4); + + test_unaligned_store(2, 1); + test_unaligned_store(4, 1); + test_unaligned_store(4, 2); + test_unaligned_store(4, 3); + test_unaligned_store(8, 1); + test_unaligned_store(8, 4); + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + chk_error(sigaction(SIGBUS, &act, NULL)); + + puts(err ? "FAIL" : "PASS"); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} From 44d1829c6cb1c9bb2c9c451a63412a615acd7d54 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Mon, 13 Jul 2026 13:07:09 -0700 Subject: [PATCH 047/860] target/hexagon: fix get_phys_addr_debug with in-page offset As documented: * @get_phys_addr_debug: Callback for obtaining a physical address. * This must be able to handle a non-page-aligned address, and will * return the physical address corresponding to that address. When MMU is enabled, hexagon_cpu_get_phys_addr_debug() returns the physical address page-aligned, not corrected to reflect the exact byte the virtual addr maps to within the page. Let's fix that. The MMU-disabled case is already correct. This would break semihosting argument reads when it is added for Hexagon. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- target/hexagon/cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index e2af736a99..12d27ce138 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -569,7 +569,9 @@ static hwaddr hexagon_cpu_get_phys_addr_debug(CPUState *cs, vaddr addr) if (get_physical_address(env, &phys_addr, &prot, &page_size, &excp, addr, 0, mmu_idx)) { + vaddr page_offset = addr & (TARGET_PAGE_SIZE - 1); find_qemu_subpage(&addr, &phys_addr, page_size); + phys_addr += hexagon_cpu_mmu_enabled(env) ? page_offset : 0; return phys_addr; } From c4066c84cdc200ba5752280829135bc389ed7eb6 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 16 Jul 2026 12:15:53 -0700 Subject: [PATCH 048/860] target/hexagon: initialize deferred sreg-write temps to current value Unconditionally allocate and initialize the temp to the sreg's current value for every logged sreg write, rather than only allocating when need_commit is set or the register is SSR. Without this, reading a deferred sreg write within the same packet before it is committed can observe an uninitialized temp instead of the register's pre-packet value. Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- target/hexagon/translate.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 5d54ce8caa..115b691255 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -615,9 +615,10 @@ static void gen_start_packet(DisasContext *ctx) } for (i = 0; i < ctx->sreg_log_idx; i++) { int reg_num = ctx->sreg_log[i]; - if (reg_num < HEX_SREG_GLB_START && - (ctx->need_commit || reg_num == HEX_SREG_SSR)) { + if (reg_num < HEX_SREG_GLB_START) { ctx->t_sreg_new_value[reg_num] = tcg_temp_new(); + tcg_gen_mov_tl(ctx->t_sreg_new_value[reg_num], + hex_t_sreg[reg_num]); } } for (i = 0; i < NUM_GREGS; i++) { From 94a5b3ff4e371c8c04c6213e4383722bcafea2a8 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 16 Jul 2026 12:15:54 -0700 Subject: [PATCH 049/860] target/hexagon: fix PC not advancing for non-COF TB-ending packets Add hex_next_PC, a global mirroring CPUHexagonState::next_PC, and ctx->need_next_pc, so that gen_write_new_pc_addr() can write the branch target through hex_next_PC instead of hex_gpr[HEX_REG_PC] when a later unconditional write of PC is expected. gen_end_tb() then commits hex_next_PC into hex_gpr[HEX_REG_PC] at the end of the packet. Previously, non-COF instructions that still end a TB did not advance the PC, since next_PC's value was never written back into the PC register. Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- target/hexagon/genptr.c | 14 ++++++++--- target/hexagon/translate.c | 51 ++++++++++++++++++++++++++------------ target/hexagon/translate.h | 3 +++ 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index f48a27bc09..3f31037709 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -613,14 +613,22 @@ static void gen_write_new_pc_addr(DisasContext *ctx, TCGv addr, tcg_gen_brcondi_tl(cond, pred, 1, pred_false); } + /* + * If gen_end_tb() will unconditionally overwrite PC with hex_next_PC + * (because this packet has a predicated COF that may not execute), + * write the branch target there instead of directly into the PC + * global, or the overwrite in gen_end_tb() would clobber it. + */ + TCGv pc_wr = ctx->need_next_pc ? hex_next_PC : hex_gpr[HEX_REG_PC]; + if (ctx->pkt.pkt_has_multi_cof) { /* If there are multiple branches in a packet, ignore the second one */ - tcg_gen_movcond_tl(TCG_COND_NE, hex_gpr[HEX_REG_PC], + tcg_gen_movcond_tl(TCG_COND_NE, pc_wr, ctx->branch_taken, tcg_constant_tl(0), - hex_gpr[HEX_REG_PC], addr); + pc_wr, addr); tcg_gen_movi_tl(ctx->branch_taken, 1); } else { - tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], addr); + tcg_gen_mov_tl(pc_wr, addr); } if (cond != TCG_COND_ALWAYS) { diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 115b691255..199b4f8c2e 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -54,6 +54,7 @@ static const AnalyzeInsn opcode_analyze[XX_LAST_OPCODE] = { TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; TCGv hex_pred[NUM_PREGS]; TCGv hex_slot_cancelled; +TCGv hex_next_PC; TCGv hex_new_value_usr; TCGv hex_store_addr[STORES_MAX]; TCGv_i32 hex_store_width[STORES_MAX]; @@ -184,10 +185,16 @@ static void gen_goto_tb(DisasContext *ctx, unsigned tb_slot_idx, } } +static bool need_next_PC(DisasContext *ctx); + static void gen_end_tb(DisasContext *ctx) { gen_exec_counters(ctx); + if (ctx->need_next_pc) { + tcg_gen_mov_tl(hex_gpr[HEX_REG_PC], hex_next_PC); + } + if (ctx->branch_cond != TCG_COND_NEVER) { if (ctx->branch_cond != TCG_COND_ALWAYS) { TCGLabel *skip = gen_new_label(); @@ -391,17 +398,25 @@ static bool pkt_ends_tb(Packet *pkt) static bool need_next_PC(DisasContext *ctx) { - /* Check for conditional control flow or HW loop end */ - for (int i = 0; i < ctx->pkt.num_insns; i++) { - uint16_t opcode = ctx->pkt.insn[i].opcode; - if (GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) { - return true; - } - if (GET_ATTRIB(opcode, A_HWLOOP0_END) || - GET_ATTRIB(opcode, A_HWLOOP1_END)) { - return true; + Packet *pkt = &ctx->pkt; + if (pkt->pkt_has_cof || ctx->pkt_ends_tb) { + for (int i = 0; i < pkt->num_insns; i++) { + uint16_t opcode = pkt->insn[i].opcode; + if ((GET_ATTRIB(opcode, A_CONDEXEC) && GET_ATTRIB(opcode, A_COF)) || + GET_ATTRIB(opcode, A_HWLOOP0_END) || + GET_ATTRIB(opcode, A_HWLOOP1_END)) { + return true; + } } } + /* + * We end the TB on some instructions that do not change the flow (for + * other reasons). In these cases, we must set pc too, as the insn won't + * do it themselves. + */ + if (ctx->pkt_ends_tb && !check_for_attrib(pkt, A_COF)) { + return true; + } return false; } @@ -637,12 +652,14 @@ static void gen_start_packet(DisasContext *ctx) ctx->branch_taken = NULL; if (ctx->pkt.pkt_has_cof) { ctx->branch_taken = tcg_temp_new(); - if (ctx->pkt.pkt_has_multi_cof) { - tcg_gen_movi_tl(ctx->branch_taken, 0); - } - if (need_next_PC(ctx)) { - tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], next_PC); - } + } + if (ctx->pkt.pkt_has_multi_cof) { + tcg_gen_movi_tl(ctx->branch_taken, 0); + } + ctx->pkt_ends_tb = pkt_ends_tb(&ctx->pkt); + ctx->need_next_pc = need_next_PC(ctx); + if (ctx->need_next_pc) { + tcg_gen_movi_tl(hex_next_PC, next_PC); } /* Preload the predicated registers into get_result_gpr(ctx, i) */ @@ -1142,7 +1159,7 @@ static void gen_commit_packet(DisasContext *ctx) ctx->pkt.vhist_insn->generate(ctx); } - if (pkt_ends_tb(&ctx->pkt) || ctx->base.is_jmp == DISAS_NORETURN) { + if (ctx->pkt_ends_tb || ctx->base.is_jmp == DISAS_NORETURN) { gen_end_tb(ctx); } } @@ -1327,6 +1344,8 @@ void hexagon_translate_init(void) } hex_new_value_usr = tcg_global_mem_new(tcg_env, offsetof(CPUHexagonState, new_value_usr), "new_value_usr"); + hex_next_PC = tcg_global_mem_new(tcg_env, + offsetof(CPUHexagonState, next_PC), "next_PC"); for (i = 0; i < NUM_PREGS; i++) { hex_pred[i] = tcg_global_mem_new(tcg_env, diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 0a7f37d584..2fca157553 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -40,6 +40,7 @@ typedef struct DisasContext { int reg_log_idx; DECLARE_BITMAP(regs_written, TOTAL_PER_THREAD_REGS); DECLARE_BITMAP(predicated_regs, TOTAL_PER_THREAD_REGS); + bool pkt_ends_tb; bool implicit_usr_write; #ifndef CONFIG_USER_ONLY int greg_log[GREG_WRITES_MAX]; @@ -75,6 +76,7 @@ typedef struct DisasContext { DECLARE_BITMAP(insn_qregs_read, NUM_QREGS); bool pre_commit; bool need_commit; + bool need_next_pc; TCGCond branch_cond; target_ulong branch_dest; bool is_tight_loop; @@ -310,6 +312,7 @@ extern TCGv hex_gpr[TOTAL_PER_THREAD_REGS]; extern TCGv hex_pred[NUM_PREGS]; extern TCGv hex_slot_cancelled; extern TCGv hex_new_value_usr; +extern TCGv hex_next_PC; extern TCGv hex_store_addr[STORES_MAX]; extern TCGv_i32 hex_store_width[STORES_MAX]; extern TCGv hex_store_val32[STORES_MAX]; From 260d15c45b938511ade1992d7ff3efe47b7519bf Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Thu, 16 Jul 2026 12:15:55 -0700 Subject: [PATCH 050/860] tests/functional/hexagon: add arch_tests functional test Add new test cases from hexagon-arch-tests to exercise exceptions, guest mode. Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- MAINTAINERS | 1 + tests/functional/hexagon/meson.build | 5 ++ tests/functional/hexagon/test_arch_tests.py | 57 +++++++++++++++++++++ tests/functional/meson.build | 1 + 4 files changed, 64 insertions(+) create mode 100644 tests/functional/hexagon/meson.build create mode 100755 tests/functional/hexagon/test_arch_tests.py diff --git a/MAINTAINERS b/MAINTAINERS index ec56e5c557..74fc43f5f2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1357,6 +1357,7 @@ F: hw/hexagon/ F: include/hw/hexagon/ F: configs/devices/hexagon-softmmu/default.mak F: docs/system/hexagon/ +F: tests/functional/hexagon/ F: docs/devel/hexagon-sys.rst LoongArch Machines diff --git a/tests/functional/hexagon/meson.build b/tests/functional/hexagon/meson.build new file mode 100644 index 0000000000..2b5a1a8f26 --- /dev/null +++ b/tests/functional/hexagon/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_hexagon_system_thorough = [ + 'arch_tests', +] diff --git a/tests/functional/hexagon/test_arch_tests.py b/tests/functional/hexagon/test_arch_tests.py new file mode 100755 index 0000000000..54a1412c59 --- /dev/null +++ b/tests/functional/hexagon/test_arch_tests.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset +from qemu_test.cmd import wait_for_console_pattern + + +class ArchTestsUart(QemuSystemTest): + """ + Hexagon architecture verification tests + + These are bare-metal tests from hexagon-arch-tests that exercise + system functionality. + + Tests output results via UART. + """ + timeout = 60 + + ASSET_TARBALL = Asset( + "https://github.com/qualcomm/qemu-hexagon-testing/releases/" + "download/v0.2.5/arch_tests_uart.tar.gz", + "edb4f37b877a3a72a0e10920477458a43b40045d34398fee8cb763fefd342f4f", + ) + + def run_uart_test(self, test_name: str, + machine: str = "virt") -> None: + """ + Run an arch test binary and verify PASS via UART console output. + """ + self.set_machine(machine) + self.archive_extract(self.ASSET_TARBALL) + target_bin = self.scratch_file('arch_tests_uart_package', + 'bin', test_name) + self.vm.set_console() + self.set_vm_arg("-display", "none") + self.set_vm_arg("-kernel", target_bin) + self.vm.launch() + wait_for_console_pattern(self, "PASS") + + def test_exceptions(self) -> None: + """Tests exception delivery for trap instructions, privilege + violations, and verifies SSR cause codes and ELR values. + """ + self.run_uart_test("test_exceptions") + + def test_guest_mode(self) -> None: + """Tests guest mode entry/exit via CCR configuration, verifying + GSR fields, GELR, and guest event vector table dispatch. + """ + self.run_uart_test("test_guest_mode") + + +if __name__ == "__main__": + QemuSystemTest.main() diff --git a/tests/functional/meson.build b/tests/functional/meson.build index c158197c4b..c7362dd00e 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -14,6 +14,7 @@ subdir('aarch64') subdir('alpha') subdir('arm') subdir('avr') +subdir('hexagon') subdir('hppa') subdir('i386') subdir('loongarch64') From c0e370474b020e4740c85c246e26ec8af1d9d53d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 13 Jul 2026 15:14:29 +0100 Subject: [PATCH 051/860] linux-user/sparc: Take pending signals in sparc64_set_context() Every callsite of block_signals() checks its return value, except the one in sparc64_set_context(). Generally you need to check, because the standard pattern is: if (block_signals()) { return -QEMU_ERESTARTSYS; } /* do some blocking syscall */ and we need to take any pending signal before we do the blocking operation, not afterwards. The use in sparc64_set_context() doesn't do this. It doesn't have to because the operations it is doing aren't blocking, so it won't get into "we didn't take the signal that we should have" races that blocking syscalls do. But it does make this way of updating the signal mask inconsistent with how we do it in do_sigprocmask(). do_sigprocmask() does the usual "return -QEMU_ERESTARTSYS", so a pending signal that was not blocked by the old signal mask and which will be blocked by the new mask we're about to install will be taken before we change the mask. sparc64_set_context() doesn't check the return value, so we won't take that pending signal. That's not wrong, because it just means the signal lost the race with the executing code. But it seems clearer to behave the same way as do_sigprocmask(), not differently. Make sparc64_set_context() check the return value of block_signals() and return early if there's a pending signal to take. We don't need to return a separate return code to indicate this because the main loop handles it the same either way. Coverity CID: 1660058 Fixes: e0f0ce88eb9 ("linux-user/sparc: call block_signals() before set_sigmask() in setcontext") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Matt Turner Signed-off-by: Helge Deller --- linux-user/sparc/cpu_loop.c | 10 ++++++++++ linux-user/sparc/signal.c | 30 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 0aacda9448..eaf388c167 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -282,6 +282,16 @@ void cpu_loop (CPUSPARCState *env) break; case TT_TRAP + 0x6f: flush_windows(env); + /* + * If we have a pending signal, sparc64_set_context() may + * return early without changing register state (like a + * syscall that returns -QEMU_ERESTARTSYS). We will then + * take the pending signal via process_pending_signals() + * below and eventually re-execute the trap. We don't need + * the function to return a different value for the + * "restart" case because this main loop code does the + * same thing in both cases. + */ sparc64_set_context(env); break; #endif diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index ba692c3123..4baf983ed8 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -594,6 +594,27 @@ void sparc64_set_context(CPUSPARCState *env) unsigned int i; unsigned char fenab; + if (env->regwptr[WREG_O1]) { + /* + * We're going to set the signal mask; we need to call + * block_signals() first, so that process_pending_signals() is + * guaranteed to run after the mask change. Without this, a + * guest signal that is pending-and-blocked at setcontext time + * is left undelivered even after its mask bit is cleared, + * because signal_pending stays 0 and the post-trap + * process_pending_signals() loop never enters. + * + * If block_signals() returns true, this means we have a + * pending signal that we could take now; we return early so + * the cpu_loop takes that signal. Eventually the guest will + * re-execute the trap insn and we'll come back here to have + * another go at set_context. This is the same way that + * do_sigprocmask() handles setting the signal mask. + */ + if (block_signals()) { + return; + } + } ucp_addr = env->regwptr[WREG_O0]; if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { goto do_sigsegv; @@ -619,15 +640,6 @@ void sparc64_set_context(CPUSPARCState *env) } } target_to_host_sigset_internal(&set, &target_set); - /* - * set_sigmask() requires the caller to have first called - * block_signals() so that process_pending_signals() is guaranteed - * to run after the mask change. Without this, a guest signal that - * is pending-and-blocked at setcontext time is left undelivered - * even after its mask bit is cleared, because signal_pending stays - * 0 and the post-trap process_pending_signals() loop never enters. - */ - block_signals(); set_sigmask(&set); } env->pc = pc; From 12d8ee9d5358cd8cdd2c2193081b4d7fea4b6098 Mon Sep 17 00:00:00 2001 From: Feifan Qian Date: Fri, 24 Apr 2026 13:20:00 +0200 Subject: [PATCH 052/860] hw/usb/xhci: clamp interval exponent to avoid UB shift in xhci_init_epctx() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xHCI endpoint context dword 0 bits 23:16 ("Interval") are written by the guest and passed directly as the shift amount in: epctx->interval = 1 << ((ctx[0] >> 16) & 0xff); The shift amount can be 0-255. Shifting a 32-bit `int` left by >= 32 is undefined behaviour under C11 §6.5.7p4. With UBSan (halt_on_error=1) this causes QEMU to abort; with aggressive compiler optimisations that assume UB is unreachable the result is unpredictable. Clamp the exponent to [0, 18] with MIN() before the shift, and use `1u` (unsigned) to avoid shifting a signed integer. The xHCI specification defines a maximum meaningful Interval value of 18 for most endpoint types; thus clamping to 18 is a safe fix that preserves the full unsigned 32-bit range for any compliant value. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3703 Reported-by: Feifan Qian Signed-off-by: Feifan Qian [thuth: Clamp to 18 instead of 31] Signed-off-by: Thomas Huth --- hw/usb/hcd-xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index c7e050e38f..47b7484d53 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1121,7 +1121,7 @@ static void xhci_init_epctx(XHCIEPContext *epctx, epctx->ring.ccs = ctx[2] & 1; } - epctx->interval = 1 << ((ctx[0] >> 16) & 0xff); + epctx->interval = 1u << MIN((ctx[0] >> 16) & 0xffu, 18u); } static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid, From 054ef33f674cdc6cb87df599790d99cfcda363cc Mon Sep 17 00:00:00 2001 From: Xiangfeng Cai Date: Thu, 18 Jun 2026 19:01:18 +0800 Subject: [PATCH 053/860] hw/usb/hcd-xhci-pci: break host link cycle so device_finalize() runs on unplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xHCI PCI wrapper embeds an xhci-core child via object_initialize_child() and, in usb_xhci_pci_realize(), points the child's "host" link back at the PCI device: object_property_set_link(OBJECT(&s->xhci), "host", OBJECT(s), NULL); "host" is a DEFINE_PROP_LINK property, which qdev registers as an OBJ_PROP_LINK_STRONG link. A strong link takes a reference on its target, so this creates a refcount cycle: the PCI device owns the child, and the child's strong link pins the PCI device. On unplug (guest ACPI eject or QMP device_del), pci_qdev_unrealize() calls pc->exit() but never unrealizes the no-bus child. object_unparent() then drops only the parent/bus references, leaving the link reference in place. The PCI device stays at refcount 1 forever, so object_finalize()/device_finalize() is never reached. Symptom observed under gdb after eject: p *((Object *)dev) => ref = 1, parent = 0x0, realized = false p ((XHCIPciState *)dev)->xhci.hostOpaque => points back at dev Fix usb_xhci_pci_exit() to tear down the embedded child explicitly: unrealize it first (so the set-link-before-realize check passes), then clear the "host" link. This releases the strong reference, lets the PCI device refcount reach 0, and allows device_finalize() to run. Fixes: 8ddab8dd3d81 ("usb/hcd-xhci: Split pci wrapper for xhci base model") Signed-off-by: Xiangfeng Cai Acked-by: Marc-André Lureau Message-ID: <20260618110119.3084296-2-caixiangfeng@bytedance.com> Signed-off-by: Thomas Huth --- hw/usb/hcd-xhci-pci.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index c5446a4a5e..b124251ae3 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -196,6 +196,18 @@ static void usb_xhci_pci_exit(PCIDevice *dev) && dev->msix_entry_used) { msix_uninit(dev, &s->xhci.mem, &s->xhci.mem); } + /* + * The embedded xhci-core child holds a strong "host" link back to this + * PCI device (set in usb_xhci_pci_realize()), forming a refcount cycle: + * the PCI device owns the child, and the child's strong link pins the PCI + * device. On unplug, object_unparent() only drops the parent/bus refs, so + * the link ref keeps this device at refcount 1 forever and + * device_finalize() never runs. Unrealize the child first (so the + * realized-check in set_link passes), then clear the link to break the + * cycle. + */ + qdev_unrealize(DEVICE(&s->xhci)); + object_property_set_link(OBJECT(&s->xhci), "host", NULL, &error_abort); } static const VMStateDescription vmstate_xhci_pci = { From ba54d7e66e251764f06d6231c5832d7c7239aa8b Mon Sep 17 00:00:00 2001 From: Xiangfeng Cai Date: Thu, 18 Jun 2026 19:01:19 +0800 Subject: [PATCH 054/860] tests/qtest: add xhci-pci unplug finalize regression test Add a qtest that hot-adds an nec-usb-xhci controller, requests unplug, resets the system to process the request, and waits for DEVICE_DELETED. This covers the xHCI PCI host-link refcount cycle by verifying that device_finalize() runs after unplug. Signed-off-by: Xiangfeng Cai Message-ID: <20260618110119.3084296-3-caixiangfeng@bytedance.com> Signed-off-by: Thomas Huth --- tests/qtest/usb-hcd-xhci-test.c | 67 +++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/qtest/usb-hcd-xhci-test.c b/tests/qtest/usb-hcd-xhci-test.c index 0cccfd85a6..b58fa1e2da 100644 --- a/tests/qtest/usb-hcd-xhci-test.c +++ b/tests/qtest/usb-hcd-xhci-test.c @@ -10,6 +10,72 @@ #include "qemu/osdep.h" #include "libqtest-single.h" #include "libqos/usb.h" +#include "qobject/qdict.h" + +static void wait_device_deleted_event(QTestState *qtest, const char *id) +{ + QDict *resp, *data; + const char *device; + + /* + * Other devices might get removed along with the removed device. Skip + * these. The device of interest will be the last one. + */ + for (;;) { + resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED"); + data = qdict_get_qdict(resp, "data"); + device = data ? qdict_get_try_str(data, "device") : NULL; + if (device && !strcmp(device, id)) { + qobject_unref(resp); + break; + } + qobject_unref(resp); + } +} + +/* + * Regression test for the xHCI-PCI "host" strong-link reference cycle. + * + * The xHCI PCI wrapper embeds an xhci-core child whose strong "host" link + * points back at the PCI device, forming a refcount cycle. If + * usb_xhci_pci_exit() does not break that cycle, the device's refcount never + * reaches 0 on unplug, device_finalize() never runs, and therefore the + * DEVICE_DELETED event (emitted from device_finalize()) is never sent. + * + * This test hot-plugs an xHCI controller into an ACPI-hotpluggable bus, + * requests its removal and waits for DEVICE_DELETED. Without the fix the event + * is never delivered (device_finalize() is blocked), so the test would + * hang/time out. + */ +static void test_xhci_unplug_finalize(void) +{ + QTestState *qtest; + const char *arch = qtest_get_arch(); + + if (strcmp(arch, "i386") != 0 && strcmp(arch, "x86_64") != 0) { + g_test_skip("Test only runs on x86 (ACPI PCI hotplug)"); + return; + } + if (!qtest_has_device("nec-usb-xhci")) { + g_test_skip("Device nec-usb-xhci not available"); + return; + } + + qtest = qtest_initf("-machine pc"); + + qtest_qmp_device_add(qtest, "nec-usb-xhci", "xhci-finalize", "{}"); + + /* + * Request device removal. As the guest is not running, the unplug request + * won't be processed until the next system reset, which performs the + * removal and triggers device_finalize() (and thus DEVICE_DELETED). + */ + qtest_qmp_device_del_send(qtest, "xhci-finalize"); + qtest_system_reset_nowait(qtest); + wait_device_deleted_event(qtest, "xhci-finalize"); + + qtest_quit(qtest); +} static void test_xhci_hotplug(void) { @@ -50,6 +116,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); + qtest_add_func("/xhci/pci/unplug/finalize", test_xhci_unplug_finalize); if (qtest_has_device("usb-uas")) { qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); } From 9bf52d056a03cd3768caccbc4c88fcc34be26c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 14 Jul 2026 22:57:17 +0400 Subject: [PATCH 055/860] usbredir: fix use-after-free on buffered bulk packet overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When usbredir_buffered_bulk_packet() splits a multi-fragment buffered bulk packet into max-packet-size chunks, only the final fragment owns the shared parser allocation (via free_on_destroy). If bufp_alloc() drops the final fragment due to queue overflow, it frees the backing buffer while earlier fragments already queued still hold interior pointers into it. Subsequent guest bulk-IN transfers then read from freed heap memory. Fix this by tracking how many fragments were queued during the current packet. When bufp_alloc() fails, remove all already-queued fragments from the tail of the endpoint queue before breaking out of the loop. If the dropped fragment was non-final, free the data buffer explicitly since no fragment took ownership. Fixes: CVE-2026-15705 Fixes: b2d1fe67d09d ("usbredir: Add support for buffered bulk input (v2)") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3808 Reported-by: Feifan Qian Signed-off-by: Marc-André Lureau Message-ID: <20260714185717.1156157-1-marcandre.lureau@redhat.com> Signed-off-by: Thomas Huth --- hw/usb/redirect.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index bde821e214..284bcbdb34 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -2138,7 +2138,7 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, USBRedirDevice *dev = priv; uint8_t status, ep = buffered_bulk_packet->endpoint; void *free_on_destroy; - int i, len; + int i, len, queued = 0; DPRINTF("buffered-bulk-in status %d ep %02X len %d id %"PRIu64"\n", buffered_bulk_packet->status, ep, data_len, id); @@ -2169,8 +2169,24 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, /* bufp_alloc also adds the packet to the ep queue */ r = bufp_alloc(dev, data + i, len, status, ep, free_on_destroy); if (r) { + /* + * Earlier fragments from this packet are in the queue + * with interior pointers into data. If the dropped + * fragment was the final one, bufp_alloc already freed + * data so those pointers are dangling. Remove them. + */ + while (queued > 0) { + struct buf_packet *bufp; + bufp = QTAILQ_LAST(&dev->endpoint[EP2I(ep)].bufpq); + bufp_free(dev, bufp, ep); + queued--; + } + if (!free_on_destroy) { + free(data); + } break; } + queued++; } if (dev->endpoint[EP2I(ep)].pending_async_packet) { From a002485bfef184f04a58adfd2a848c19f583f7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 16 Jul 2026 18:11:07 +0400 Subject: [PATCH 056/860] usbredir: fix infinite loop and SIGFPE with zero max_packet_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A malicious usbredir peer can send an ep_info message resetting max_packet_size to 0 after bulk receiving has started. This causes: - infinite loop in usbredir_buffered_bulk_packet() where the splitting loop increments by max_packet_size (0) - SIGFPE in usbredir_buffered_bulk_in_complete_ftdi() from modulo by 0 - SIGFPE in usbredir_handle_buffered_bulk_in_data() from division by 0 when computing bytes_per_transfer Fix by stopping and disabling bulk receiving in usbredir_ep_info() when max_packet_size is set to 0. Add post-load check, and assert() for the invariant. Fixes: CVE-2026-63319 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3995 Reported-by: Tristan @TristanInSec Signed-off-by: Marc-André Lureau Reviewed-by: Thomas Huth Message-ID: <20260716141107.3597076-1-marcandre.lureau@redhat.com> Signed-off-by: Thomas Huth --- hw/usb/redirect.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 284bcbdb34..dfd9e8bb50 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -690,6 +690,7 @@ static void usbredir_buffered_bulk_in_complete_ftdi(USBRedirDevice *dev, struct buf_packet *bulkp; int count; + assert(maxp != 0); while ((bulkp = QTAILQ_FIRST(&dev->endpoint[EP2I(ep)].bufpq)) && p->actual_length < p->iov.size && p->status == USB_RET_SUCCESS) { if (bulkp->len < 2) { @@ -739,6 +740,7 @@ static void usbredir_handle_buffered_bulk_in_data(USBRedirDevice *dev, .stream_id = 0, .no_transfers = 5, }; + assert(dev->endpoint[EP2I(ep)].max_packet_size != 0); /* Round bytes_per_transfer up to a multiple of max_packet_size */ bpt = 512 + dev->endpoint[EP2I(ep)].max_packet_size - 1; bpt /= dev->endpoint[EP2I(ep)].max_packet_size; @@ -793,6 +795,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, } if (dev->endpoint[EP2I(ep)].bulk_receiving_enabled) { + assert(maxp != 0); if (size != 0 && (size % maxp) == 0) { usbredir_handle_buffered_bulk_in_data(dev, p, ep); return; @@ -1796,6 +1799,17 @@ static void usbredir_ep_info(void *priv, if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_ep_info_max_packet_size)) { dev->endpoint[i].max_packet_size = ep_info->max_packet_size[i]; + if (ep_info->max_packet_size[i] == 0 && + dev->endpoint[i].bulk_receiving_enabled) { + USBPacket *p = dev->endpoint[i].pending_async_packet; + usbredir_stop_bulk_receiving(dev, I2EP(i)); + dev->endpoint[i].bulk_receiving_enabled = 0; + if (p != NULL) { + dev->endpoint[i].pending_async_packet = NULL; + p->status = USB_RET_IOERROR; + usb_packet_complete(&dev->dev, p); + } + } } #if USBREDIR_VERSION >= 0x000700 if (usbredirparser_peer_has_cap(dev->parser, @@ -2156,6 +2170,7 @@ static void usbredir_buffered_bulk_packet(void *priv, uint64_t id, } /* Data must be in maxp chunks for buffered_bulk_add_*_data_to_packet */ + assert(dev->endpoint[EP2I(ep)].max_packet_size != 0); len = dev->endpoint[EP2I(ep)].max_packet_size; status = usb_redir_success; free_on_destroy = NULL; @@ -2239,6 +2254,15 @@ static int usbredir_post_load(void *priv, int version_id) usbredir_setup_usb_eps(dev); usbredir_check_bulk_receiving(dev); + for (int i = 0; i < MAX_ENDPOINTS; i++) { + if (dev->endpoint[i].bulk_receiving_started && + dev->endpoint[i].max_packet_size == 0) { + error_report("usbredir: endpoint %d has bulk receiving started " + "with zero max_packet_size", i); + return -EINVAL; + } + } + return 0; } From 4daeaa2fe0f979b70fb3e54cc5661aadbffcd311 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 15 Jul 2026 22:33:57 +0200 Subject: [PATCH 057/860] hw/usb/hcd-xhci: Fix guest-triggerable assert() in xhci_find_stream() The assert() statement in xhci_find_stream() can be triggered by the guest (see bug tickets #273, #3895 and #3988 on gitlab.com). Turn it into a qemu_log_mask() instead to fix this problem. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/273 Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth Message-ID: <20260715203357.424556-1-thuth@redhat.com> --- hw/usb/hcd-xhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 47b7484d53..b94249993a 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1009,7 +1009,12 @@ static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx, dma_addr_t base; uint32_t ctx[2], sct; - assert(streamid != 0); + if (!streamid) { + qemu_log_mask(LOG_GUEST_ERROR, "xhci: stream ID is zero\n"); + *cc_error = CC_INVALID_STREAM_ID_ERROR; + return NULL; + } + if (epctx->lsa) { if (streamid >= epctx->nr_pstreams) { *cc_error = CC_INVALID_STREAM_ID_ERROR; From cc479aa89743655a725ce29328bcd0585a65983a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Sun, 19 Jul 2026 08:15:28 +0200 Subject: [PATCH 058/860] hw/usb/hcd-xhci-sysbus: Fix OOB heap access in xhci_sysbus_intr_raise() Some machines like the microvm machine instantiate a "sysbus-xhci" device with just 1 interrupt (by setting the "intrs" property to 1). xhci_sysbus_realize() then only allocates the s->irq array with one entry. When the guest writes to the ERDP register of a corresponding XHCI "interrupter", the generic XHCI code calls the xhci_sysbus_intr_raise() function with n > 1, and this function then calls qemu_set_irq() with s->irq[n] pointing to a bad heap address. The qemu_set_irq() then tries to call an IRQ handler via a function pointer in that heap space. This either causes QEMU to die with a segmentation fault (if it's a bad address), or even worse runs some unexpected code if the destination of the pointer is executable code. Looking at the xHCI spec, it is up to the implementation of the host controller how many interrupters are available. So if we only support one or some few interrupters, the registers of the other interrupters should not do anything, i.e. reads should result in zeros and writes should be completely ignored. (big thanks to Peter Maydell for helping with the analyzation of the correct way to fix this here) This way, the xhci_sysbus_intr_raise() function cannot be called with an invalid interrupt number anymore. But for good measure, also add an assert() statement to the xhci_sysbus_intr_raise() function to prevent that similar problems with calling arbitrary function pointers on the heap could occur again. Fixes: CVE-2026-16043 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4001 Reported-by: Tristan Madani Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth Message-ID: <20260719061528.15587-1-thuth@redhat.com> --- hw/usb/hcd-xhci-sysbus.c | 1 + hw/usb/hcd-xhci.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index 19664c5985..bbdd5fd64a 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -20,6 +20,7 @@ static bool xhci_sysbus_intr_raise(XHCIState *xhci, int n, bool level) { XHCISysbusState *s = container_of(xhci, XHCISysbusState, xhci); + assert(n < xhci->numintrs); qemu_set_irq(s->irq[n], level); return false; diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index b94249993a..569386b8cf 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3044,6 +3044,12 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg, } } else { int v = (reg - 0x20) / 0x20; + + if (v >= xhci->numintrs) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: read from nonexistent interrupter %i\n", v); + goto out_trace; + } XHCIInterrupter *intr = &xhci->intr[v]; switch (reg & 0x1f) { case 0x00: /* IMAN */ @@ -3070,6 +3076,7 @@ static uint64_t xhci_runtime_read(void *ptr, hwaddr reg, } } +out_trace: trace_usb_xhci_runtime_read(reg, ret); return ret; } @@ -3087,7 +3094,13 @@ static void xhci_runtime_write(void *ptr, hwaddr reg, trace_usb_xhci_unimplemented("runtime write", reg); return; } + v = (reg - 0x20) / 0x20; + if (v >= xhci->numintrs) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: write to nonexistent interrupter %i\n", v); + return; + } intr = &xhci->intr[v]; switch (reg & 0x1f) { From 94bd02908a16a5ca0498538615afa9f37337ec87 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 11 Jun 2026 08:54:38 -0400 Subject: [PATCH 059/860] iotests/108: avoid leaking FUSE mount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QEMU s390x CI gitlab-runner recently broke because qemu-iotests 108 FUSE mount leaked and prevented the setup of the gitlab-runner environment: chmod: cannot access '/home/gitlab-runner/builds/P3MFS4LUf/0/qemu-project/qemu/build/scratch/qcow2-file-108/fuse-export': Transport endpoint is not connected https://gitlab.com/qemu-project/qemu/-/jobs/14796143507#L11 Always umount the FUSE export when cleaning up to prevent the leak. Closes: https://gitlab.com/qemu-project/qemu/-/work_items/3541 Cc: Hanna Czenczek Reviewed-by: Daniel P. Berrangé Message-ID: <20260611125438.194479-1-stefanha@redhat.com> Signed-off-by: Stefan Hajnoczi --- tests/qemu-iotests/108 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/108 b/tests/qemu-iotests/108 index 54e935acf2..bf808b3512 100755 --- a/tests/qemu-iotests/108 +++ b/tests/qemu-iotests/108 @@ -34,8 +34,8 @@ _cleanup() if [ -f "$TEST_DIR/qsd.pid" ]; then qsd_pid=$(cat "$TEST_DIR/qsd.pid") kill -KILL "$qsd_pid" - fusermount -u "$TEST_DIR/fuse-export" &>/dev/null fi + fusermount -u "$TEST_DIR/fuse-export" &>/dev/null rm -f "$TEST_DIR/fuse-export" } trap "_cleanup; exit \$status" 0 1 2 3 15 From 8833ff95eaf347f79b61d857cc5eed32ec6d2a0b Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Mon, 20 Jul 2026 19:05:29 +0100 Subject: [PATCH 060/860] hw/char: imx_serial: add missing migration state The imx_serial vmstate is missing the ucr2 field. This register includes important state like the transmit enable and receive enable bits, so it's likely that after a migration the UART will be in a completely broken state. This bug has been present ever since the UART code was first added to QEMU. Add ucr2 from imx_serial to vmstate, and increment the version_id. This is a migration compatibility break, but this UART is only used in the various imx-based boards, where we are OK with compat breaks. Migrating on sabrelite can reproduce this issue: 1. Prepare the U-Boot required for sabrelite. (according to sabrelite.rst) 2. Compile qemu $ mkdir build && cd build && ../configure --target-list="arm-softmmu" && make -j4 3. Start sabrelite and prepare for migration $ ./build/qemu-system-arm -M sabrelite \ -smp 1 -m 1G -display none -serial null -serial mon:stdio \ -kernel ~/u-boot 4. Enter qemu monitor after uboot. (ctrl + a + c) (qemu) stop (qemu) xp /4wx 0x021e8084 021e8084: 0x00004027 0x00000784 0x00008000 0x00000a01 (qemu) migrate -d file:vmstate (qemu) q Load the migrated vmstate, before repairing: $ ./build/qemu-system-arm -M sabrelite \ -smp 1 -m 1G -display none -serial null -serial mon:stdio \ -kernel ~/u-boot -incoming file:vmstate (ctrl + a + c) QEMU 11.0.50 monitor - type 'help' for more information (qemu) xp /4wx 0x021e8084 021e8084: 0x00000004 0x00000784 0x00008000 0x00000a01 (qemu) q It can be found that the data for address 0x021e8084 (register of usr2 in imx_serial of sabrelite) is not the data before the migration. After being repaired: $ ./build/qemu-system-arm -M sabrelite \ -smp 1 -m 1G -display none -serial null -serial mon:stdio \ -kernel ~/u-boot -incoming file:vmstate (ctrl + a + c) QEMU 11.0.50 monitor - type 'help' for more information (qemu) xp /4wx 0x021e8084 021e8084: 0x00004027 0x00000784 0x00008000 0x00000a01 Cc: qemu-stable@nongnu.org Fixes: 40b6f91151 ("i.MX: UART support") Signed-off-by: Tao Ding Message-id: 20260715131819.14827-2-dingtao0430@163.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/char/imx_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 080b7f6331..fb41ee2ac5 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -43,14 +43,15 @@ static const VMStateDescription vmstate_imx_serial = { .name = TYPE_IMX_SERIAL, - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (const VMStateField[]) { VMSTATE_FIFO32(rx_fifo, IMXSerialState), VMSTATE_TIMER(ageing_timer, IMXSerialState), VMSTATE_UINT32(usr1, IMXSerialState), VMSTATE_UINT32(usr2, IMXSerialState), VMSTATE_UINT32(ucr1, IMXSerialState), + VMSTATE_UINT32(ucr2, IMXSerialState), VMSTATE_UINT32(uts1, IMXSerialState), VMSTATE_UINT32(onems, IMXSerialState), VMSTATE_UINT32(ufcr, IMXSerialState), From 83927347227c91dd659e7ed718d656ed0d551ab6 Mon Sep 17 00:00:00 2001 From: Jay Chang Date: Mon, 20 Jul 2026 19:05:29 +0100 Subject: [PATCH 061/860] hw/net/xilinx_axienet: Fix PHY register 17 link status reporting The Marvell 88E1111 PHY register 17 (PHY Specific Status Register) bit 10 reports real-time link status. Previously, this register returned a fixed value of 0x8800 with bit 10 always cleared, causing U-Boot to always detect "No link" even when the link was up. Signed-off-by: Jay Chang Reviewed-by: Frank Chang Message-id: 20260713064617.85374-1-jay.chang@sifive.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/net/xilinx_axienet.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 970732b162..9f5f65ecfa 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -103,6 +103,9 @@ static unsigned int tdk_read(struct PHY *phy, unsigned int req) case 17: /* Marvell PHY on many xilinx boards. */ r = 0x8000; /* 1000Mb */ + if (phy->link) { + r |= 0x0400; /* Link is up */ + } break; case 18: { From 4ef00a9fc4a4e77d007c94d779f041b74adff951 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:29 +0100 Subject: [PATCH 062/860] target/arm: GICv5 cpuif: gicr_cdia_read() ppibit should be 64 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In gicr_cdia_read() we turn a PPI interrupt ID into a register index and a bit mask with a 1 for the bit we want to change: ppireg = id / 64; ppibit = 1ULL << (id % 64); However, we used the wrong type for ppibit, making it a uint32_t. If 'id' is too large we'll shift off the end, so we won't ever update the state of PPIs with indexes above 31. This didn't have any visible effects because the currently allocated architected PPIs are indexes 0..31, so you'd only see this if for some reason a guest was manually marking as pending a PPI in 32..63. Fix the type of ppibit to the intended 64 bit width. Fixes: 3f79212abae89 ("target/arm: GICv5 cpuif: Implement GICR CDIA command") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20260714091806.3568281-2-peter.maydell@linaro.org --- target/arm/tcg/gicv5-cpuif.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index 1cdd4103d0..8e2a08fdb6 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -633,7 +633,8 @@ static uint64_t gicr_cdia_read(CPUARMState *env, const ARMCPRegInfo *ri) switch (type) { case GICV5_PPI: { - uint32_t ppireg, ppibit; + uint32_t ppireg; + uint64_t ppibit; assert(id < GICV5_NUM_PPIS); ppireg = id / 64; From 2a9847868fcf5e93671c4d759db685474d2d7342 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:29 +0100 Subject: [PATCH 063/860] target/arm: GICv5 cpuif: Writes to ICC_APR_EL1 can change IRQ/FIQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ICC_APR_EL1 register values change the current running priority, which we use in our calculations in gic_hppi(). Changing them can affect whether we have a HPPI, and so the value of the IRQ/FIQ outputs. We need to trigger an update in the register writefn. Fixes: 9bd90bddb79 ("target/arm: GICv5 cpuif: Signal IRQ or FIQ") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20260714091806.3568281-3-peter.maydell@linaro.org --- target/arm/tcg/gicv5-cpuif.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index 8e2a08fdb6..a6e6c44084 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -495,6 +495,7 @@ static void gic_icc_apr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv5Domain domain = gicv5_logical_domain(env); value &= 0xffffffff; env->gicv5_cpuif.icc_apr[domain] = value; + gicv5_update_irq_fiq(env); } static uint64_t gic_icc_apr_el1_read(CPUARMState *env, const ARMCPRegInfo *ri) From a148ca762788a47d35f307411c6611c80ea07d35 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 064/860] target/ppc: Remove unused helper_lscbx() The helper_lscbx() function isn't called anywhere. It used to be used by the PPC 601 CPU support; we removed that in commit 005b69fdcc in 2022 but missed this helper function. Fixes: 005b69fdcc ("target/ppc: Remove PowerPC 601 CPUs") Signed-off-by: Peter Maydell Reviewed-by: Amit Machhiwal Message-id: 20260709095056.1803725-1-peter.maydell@linaro.org --- target/ppc/helper.h | 1 - target/ppc/mem_helper.c | 28 ---------------------------- 2 files changed, 29 deletions(-) diff --git a/target/ppc/helper.h b/target/ppc/helper.h index e99c8c824b..6b2d19a3ad 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -54,7 +54,6 @@ DEF_HELPER_FLAGS_2(dcbzl, TCG_CALL_NO_WG, void, env, tl) #endif DEF_HELPER_FLAGS_2(icbi, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_FLAGS_2(icbiep, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) DEF_HELPER_4(DIVDEU, i64, env, i64, i64, i32) diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 119dc1df23..787ba2cdec 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -367,34 +367,6 @@ void helper_icbiep(CPUPPCState *env, target_ulong addr) #endif } -/* XXX: to be tested */ -target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, - uint32_t ra, uint32_t rb) -{ - int i, c, d; - - d = 24; - for (i = 0; i < xer_bc; i++) { - c = cpu_ldub_data_ra(env, addr, GETPC()); - addr = addr_add(env, addr, 1); - /* ra (if not 0) and rb are never modified */ - if (likely(reg != rb && (ra == 0 || reg != ra))) { - env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); - } - if (unlikely(c == xer_cmp)) { - break; - } - if (likely(d != 0)) { - d -= 8; - } else { - d = 24; - reg++; - reg = reg & 0x1F; - } - } - return i; -} - /*****************************************************************************/ /* Altivec extension helpers */ #if HOST_BIG_ENDIAN From 1f8aa4b6af88e2df4792b9a22782a6b0435b864e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 065/860] target/arm: Fix testing of raw mtx value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MTX is always a pair of bits, one for each half of the address space. Testing it like a boolean is incorrect. Introduce raw_mte_check, a mirror of the similar mte_check function that applies when MTX is passed in MTEDESC. Fixes: 8912ceced815 ("target/arm: load on canonical tag loads ext bits") Signed-off-by: Richard Henderson Message-id: 20260717161430.37264-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/tcg/mte_helper.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index dca4ef5a94..ad4ad5b4cc 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -317,6 +317,12 @@ int load_tag1(uint64_t ptr, uint8_t *mem) return extract32(*mem, ofs, 4); } +/* Like mtx_check, but simple mtx bit pair instead of MTEDESC. */ +static bool raw_mtx_check(unsigned mtx, unsigned bit55) +{ + return (mtx >> bit55) & 1; +} + uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx) { int mmu_idx = arm_env_mmu_index(env); @@ -330,9 +336,11 @@ uint64_t HELPER(ldg)(CPUARMState *env, uint64_t ptr, uint64_t xt, uint32_t mtx) /* Load if page supports tags. */ if (mem) { rtag = load_tag1(ptr, mem); - } else if (mtx) { - uint64_t bit55 = extract64(ptr, 55, 1); - rtag = 0xF * bit55; + } else { + bool bit55 = extract64(ptr, 55, 1); + if (raw_mtx_check(mtx, bit55)) { + rtag = 0xF * bit55; + } } return address_with_allocation_tag(xt, rtag); @@ -387,7 +395,7 @@ static inline void do_stg(CPUARMState *env, uint64_t ptr, uint64_t xt, /* Store if page supports tags. */ if (mem) { store1(ptr, mem, allocation_tag_from_addr(xt)); - } else if (mtx) { + } else if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } } @@ -420,6 +428,7 @@ static inline void do_st2g(CPUARMState *env, uint64_t ptr, uint64_t xt, uint8_t *mem1, *mem2; check_tag_aligned(env, ptr, ra); + mtx = raw_mtx_check(mtx, extract64(ptr, 55, 1)); /* * Trap if accessing an invalid page(s). @@ -504,8 +513,8 @@ uint64_t HELPER(ldgm)(CPUARMState *env, uint64_t ptr, uint32_t mtx) /* The tag is squashed to zero if the page does not support tags. */ if (!tag_mem) { /* Load canonical value if mtx is set (untagged memory region) */ - if (mtx) { - bool bit55 = extract64(ptr, 55, 1); + bool bit55 = extract64(ptr, 55, 1); + if (raw_mtx_check(mtx, bit55)) { ret = extract64(-bit55, 0, 1 << gm_bs); shift = extract64(ptr, LOG2_TAG_GRANULE, 4) * 4; return ret << shift; @@ -573,7 +582,7 @@ void HELPER(stgm)(CPUARMState *env, uint64_t ptr, uint64_t val, uint32_t mtx) */ if (!tag_mem) { /* Storing tags to canonically tagged region: fault. */ - if (mtx) { + if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } return; @@ -630,7 +639,7 @@ void HELPER(stzgm_tags)(CPUARMState *env, uint64_t ptr, uint64_t val, if (mem) { int tag_pair = (val & 0xf) * 0x11; memset(mem, tag_pair, tag_bytes); - } else if (mtx) { + } else if (raw_mtx_check(mtx, extract64(ptr, 55, 1))) { canonical_tag_write_fail(env, ptr, ra); } } From 02cfe21f6a251df1229077af3b1c5583020a33bf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 066/860] hw/dma/soc_dma: Remove soc_dma_port_fifo support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our current single OMAP SoC doesn't call the soc_dma_port_add_fifo(), soc_dma_port_add_fifo_in() or soc_dma_port_add_fifo_out() functions. Remove them, plus the soc_dma_port_fifo handling that only those functions needed. The motivation for this is that it removes a lot of code that is careless about the fact that the largest possible DMA transfer is more bits than will fit into an "int" variable, and which does direct accesses to host memory pointers into guest backing RAM without doing bounds checks. Deleting this code means we don't have to audit and update it. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260710105907.2570621-2-peter.maydell@linaro.org --- hw/dma/soc_dma.c | 105 +-------------------------------------- include/hw/arm/soc_dma.h | 24 ++------- 2 files changed, 5 insertions(+), 124 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index d5c52b804f..8ba531feea 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -29,33 +29,6 @@ static void transfer_mem2mem(struct soc_dma_ch_s *ch) ch->paddr[1] += ch->bytes; } -static void transfer_mem2fifo(struct soc_dma_ch_s *ch) -{ - ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes); - ch->paddr[0] += ch->bytes; -} - -static void transfer_fifo2mem(struct soc_dma_ch_s *ch) -{ - ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes); - ch->paddr[1] += ch->bytes; -} - -/* This is further optimisable but isn't very important because often - * DMA peripherals forbid this kind of transfers and even when they don't, - * oprating systems may not need to use them. */ -static void *fifo_buf; -static int fifo_size; -static void transfer_fifo2fifo(struct soc_dma_ch_s *ch) -{ - if (ch->bytes > fifo_size) - fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes); - - /* Implement as transfer_fifo2linear + transfer_linear2fifo. */ - ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes); - ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes); -} - struct dma_s { struct soc_dma_s soc; int chnum; @@ -67,11 +40,6 @@ struct dma_s { enum soc_dma_port_type type; hwaddr addr; union { - struct { - void *opaque; - soc_dma_io_t fn; - int out; - } fifo; struct { void *base; size_t size; @@ -129,20 +97,7 @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( struct dma_s *dma = (struct dma_s *) ch->dma; struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]); - if (entry->type == soc_dma_port_fifo) { - while (entry < dma->memmap + dma->memmap_size && - entry->u.fifo.out != port) - entry ++; - if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port) - return soc_dma_port_other; - - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->io_fn[port] = entry->u.fifo.fn; - ch->io_opaque[port] = entry->u.fifo.opaque; - return soc_dma_port_fifo; - } else if (entry->type == soc_dma_port_mem) { + if (entry->type == soc_dma_port_mem) { if (entry->addr > ch->vaddr[port] || entry->addr + entry->u.mem.size <= ch->vaddr[port]) return soc_dma_port_other; @@ -173,15 +128,8 @@ void soc_dma_ch_update(struct soc_dma_ch_s *ch) } dst = soc_dma_ch_update_type(ch, 1); - /* TODO: use src and dst as array indices. */ if (src == soc_dma_port_mem && dst == soc_dma_port_mem) ch->transfer_fn = transfer_mem2mem; - else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_mem2fifo; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_fifo2mem; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_fifo2fifo; else ch->transfer_fn = ch->dma->transfer_fn; @@ -251,61 +199,10 @@ struct soc_dma_s *soc_dma_init(int n) } soc_dma_reset(&s->soc); - fifo_size = 0; return &s->soc; } -void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - if (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base) { - error_report("%s: FIFO at %"PRIx64 - " collides with RAM region at %"PRIx64 - "-%"PRIx64, __func__, - virt_base, entry->addr, - (entry->addr + entry->u.mem.size)); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) { - if (entry->addr == virt_base && entry->u.fifo.out == out) { - error_report("%s: FIFO at %"PRIx64 - " collides FIFO at %"PRIx64, - __func__, virt_base, entry->addr); - exit(-1); - } - - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_fifo; - entry->u.fifo.fn = fn; - entry->u.fifo.opaque = opaque; - entry->u.fifo.out = out; -} - void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, hwaddr virt_base, size_t size) { diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index bcdb91425a..e1d75bb3ba 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -25,12 +25,10 @@ struct soc_dma_s; struct soc_dma_ch_s; -typedef void (*soc_dma_io_t)(void *opaque, uint8_t *buf, int len); typedef void (*soc_dma_transfer_t)(struct soc_dma_ch_s *ch); enum soc_dma_port_type { soc_dma_port_mem, - soc_dma_port_fifo, soc_dma_port_other, }; @@ -57,8 +55,6 @@ struct soc_dma_ch_s { hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ /* Private */ void *paddr[2]; - soc_dma_io_t io_fn[2]; - void *io_opaque[2]; int running; soc_dma_transfer_t transfer_fn; @@ -82,33 +78,21 @@ struct soc_dma_s { /* Call to activate or stop a DMA channel. */ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level); -/* Call after every write to one of the following fields and before +/* + * Call after every write to one of the following fields and before * calling soc_dma_set_request(ch, 1): * ch->type[0...1], * ch->vaddr[0...1], * ch->paddr[0...1], - * or after a soc_dma_port_add_fifo() or soc_dma_port_add_mem(). */ + * or after a soc_dma_port_add_mem(). + */ void soc_dma_ch_update(struct soc_dma_ch_s *ch); /* The SoC should call this when the DMA module is being reset. */ void soc_dma_reset(struct soc_dma_s *s); struct soc_dma_s *soc_dma_init(int n); -void soc_dma_port_add_fifo(struct soc_dma_s *dma, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out); void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base, hwaddr virt_base, size_t size); -static inline void soc_dma_port_add_fifo_in(struct soc_dma_s *dma, - hwaddr virt_base, soc_dma_io_t fn, void *opaque) -{ - return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 0); -} - -static inline void soc_dma_port_add_fifo_out(struct soc_dma_s *dma, - hwaddr virt_base, soc_dma_io_t fn, void *opaque) -{ - return soc_dma_port_add_fifo(dma, virt_base, fn, opaque, 1); -} - #endif From f00bef33febb92dfc91a00bba3e4819070ee8d29 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 067/860] hw/dma/soc_dma: Simplify soc_dma_ch_update() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now we only have "mem" and "other" as soc_dma_port_type values, we can simplify soc_dma_ch_update(): either both src and dst are mem, in which case we use transfer_mem2mem and set update to 1 to tell omap_dma_transfer_setup() to update all the guest-visible src/dest/count information to indicate a completed transfer; or else we use the omap_dma_transfer_generic() function, and we set update to 0 to tell omap_dma_transfer_setup() that the transfer function will be updating the src/dest/count. Signed-off-by: Peter Maydell Message-id: 20260710105907.2570621-3-peter.maydell@linaro.org Reviewed-by: Alex Bennée --- hw/dma/soc_dma.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 8ba531feea..ff51338388 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -121,19 +121,14 @@ void soc_dma_ch_update(struct soc_dma_ch_s *ch) enum soc_dma_port_type src, dst; src = soc_dma_ch_update_type(ch, 0); - if (src == soc_dma_port_other) { + dst = soc_dma_ch_update_type(ch, 1); + if (src == soc_dma_port_other || dst == soc_dma_port_other) { ch->update = 0; ch->transfer_fn = ch->dma->transfer_fn; - return; - } - dst = soc_dma_ch_update_type(ch, 1); - - if (src == soc_dma_port_mem && dst == soc_dma_port_mem) + } else { + ch->update = 1; ch->transfer_fn = transfer_mem2mem; - else - ch->transfer_fn = ch->dma->transfer_fn; - - ch->update = (dst != soc_dma_port_other); + } } static void soc_dma_ch_freq_update(struct dma_s *s) From 68f788e70536c2653f32324795f88505f9d2ab4d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 068/860] hw/dma/soc_dma: Remove union from memmap_entry_s struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's only one field in the union inside memmap_entry_s now that we've removed the soc_dma_port_fifo handling. Simplify by removing the union. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260710105907.2570621-4-peter.maydell@linaro.org --- hw/dma/soc_dma.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index ff51338388..bbcd2bdcd5 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -39,12 +39,10 @@ struct dma_s { struct memmap_entry_s { enum soc_dma_port_type type; hwaddr addr; - union { - struct { - void *base; - size_t size; - } mem; - } u; + struct { + void *base; + size_t size; + } mem; } *memmap; int memmap_size; @@ -99,7 +97,7 @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( if (entry->type == soc_dma_port_mem) { if (entry->addr > ch->vaddr[port] || - entry->addr + entry->u.mem.size <= ch->vaddr[port]) + entry->addr + entry->mem.size <= ch->vaddr[port]) return soc_dma_port_other; /* TODO: support constant memory address for source port as used for @@ -107,7 +105,7 @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( if (ch->type[port] != soc_dma_access_const) return soc_dma_port_other; - ch->paddr[port] = (uint8_t *) entry->u.mem.base + + ch->paddr[port] = (uint8_t *) entry->mem.base + (ch->vaddr[port] - entry->addr); /* TODO: save bytes left to the end of the mapping somewhere so we * can check we're not reading beyond it. */ @@ -212,12 +210,12 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, if (entry->type == soc_dma_port_mem) { if ((entry->addr >= virt_base && entry->addr < virt_base + size) || (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base)) { + entry->addr + entry->mem.size > virt_base)) { error_report("%s: RAM at %"PRIx64 "-%"PRIx64 " collides with RAM region at %"PRIx64 "-%"PRIx64, __func__, virt_base, virt_base + size, - entry->addr, entry->addr + entry->u.mem.size); + entry->addr, entry->addr + entry->mem.size); exit(-1); } @@ -246,8 +244,8 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, entry->addr = virt_base; entry->type = soc_dma_port_mem; - entry->u.mem.base = phys_base; - entry->u.mem.size = size; + entry->mem.base = phys_base; + entry->mem.size = size; } /* TODO: port removal for ports like PCMCIA memory */ From efbf01e03fb0fa0f473898d33349cdd6f5946a92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 069/860] hw/dma/omap_dma: Be more careful about overflow in transfer setup In omap_dma_transfer_setup(), the maximum number of elements we can transfer is 0xffff * 0xffff == 0xfffe0001 (because the max frame count and max elements per frame are both 65535). However, we store total element counts in 'int' variables, and use INT_MAX as a "bigger than any valid value" sentinel, and when performing arithmetic with the total count of transferred elements we are not careful about avoiding overflows. Fix these: - use uint32_t rather than int for the local variables tracking various element and frame counts - use UINT_MAX as our sentinel - calculate new packet, element and frame counter values using arithmetic on a local uint32_t, rather than doing it in-place on local variables that are only 'int' because the actual counter registers are 16 bits - use 64-bit arithmetic when calculating how much to advance the source and dest pointers and the total dma->bytes transferred Note that since soc_dma_ch_s::bytes is only 'int' this can still overflow; we'll fix that in a subsequent patch. Signed-off-by: Peter Maydell Reviewed-by: Jim MacArthur Message-id: 20260710105907.2570621-5-peter.maydell@linaro.org --- hw/dma/omap_dma.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 77f1441498..14b310bf40 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -391,7 +391,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) struct omap_dma_reg_set_s *a; struct omap_dma_channel_s *ch = dma->opaque; struct omap_dma_s *s = dma->dma->opaque; - int frames, min_elems, elements[__omap_dma_intr_last]; + uint32_t frames, min_elems, elements[__omap_dma_intr_last]; a = &ch->active_set; @@ -403,7 +403,14 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) __func__, dma->num); } - min_elems = INT_MAX; + /* + * The maximum frame count and maximum element count are both 0xffff, + * so our worst case possible number of elements to transfer is + * 0xffff * 0xffff == 0xfffe0001. We can therefore keep element + * counts in a uint32_t and use UINT_MAX as a sentinel value for + * "not set" / "condition does not occur". + */ + min_elems = UINT_MAX; /* Check all the conditions that terminate the transfer starting * with those that can occur the soonest. */ @@ -413,7 +420,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) if (elements[id] < min_elems) \ min_elems = elements[id]; \ } else \ - elements[id] = INT_MAX; + elements[id] = UINT_MAX; /* Elements */ INTR_CHECK( @@ -465,7 +472,7 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) (a->frames - a->frame - 1) * a->elements + (a->elements - a->element)) - dma->bytes = min_elems * ch->data_type; + dma->bytes = (uint64_t)min_elems * ch->data_type; /* Set appropriate interrupts and/or deactivate channels */ @@ -528,8 +535,9 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) /* Update packet number */ if (ch->fs && ch->bs) { - a->pck_element += min_elems; - a->pck_element %= a->pck_elements; + /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */ + uint32_t new_pck_element = a->pck_element + min_elems; + a->pck_element = new_pck_element % a->pck_elements; } /* @@ -537,13 +545,15 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) * can skip part of this. */ if (dma->update) { + /* Can't overflow: worst case min_elems 0xFFFE0001 + element 0xFFFF */ + uint32_t new_element = a->element + min_elems; a->element += min_elems; - frames = a->element / a->elements; - a->element = a->element % a->elements; + frames = new_element / a->elements; + a->element = new_element % a->elements; a->frame += frames; - a->src += min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; - a->dest += min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; + a->src += (uint64_t)min_elems * a->elem_delta[0] + frames * a->frame_delta[0]; + a->dest += (uint64_t)min_elems * a->elem_delta[1] + frames * a->frame_delta[1]; /* If the channel is async, update cpc */ if (!ch->sync && frames) { From 22afad9489fadb87105d78e8fdaf51e801bef863 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 070/860] hw/dma/soc_dma: dma bytes is uint64_t The worst case number of DMA bytes that omap_dma will ask us to transfer is 0xffff * 0xffff * 4 == 0x3fff80004, which is slightly larger than fits into a uint32_t. Move the byte count to uint64_t, and adjust code that passes it around to also use uint64_t. Signed-off-by: Peter Maydell Reviewed-by: Jim MacArthur Message-id: 20260710105907.2570621-6-peter.maydell@linaro.org --- hw/dma/soc_dma.c | 6 +++++- include/hw/arm/soc_dma.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index bbcd2bdcd5..1b93aaf565 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -49,11 +49,15 @@ struct dma_s { struct soc_dma_ch_s ch[]; }; -static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) +static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, uint64_t delay_bytes) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); struct dma_s *dma = (struct dma_s *) ch->dma; + /* + * Worst case delay bytes is only slightly larger than fits into + * a 32-bit integer, so this won't overflow. + */ timer_mod(ch->timer, now + delay_bytes / dma->channel_freq); } diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index e1d75bb3ba..b5ad9743be 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -49,7 +49,7 @@ struct soc_dma_ch_s { int update; /* This should be set by dma->setup_fn(). */ - int bytes; + uint64_t bytes; /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ enum soc_dma_access_type type[2]; hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ From 3162318194472ace95700db9265f476c2a8d32f8 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 071/860] hw/dma/soc_dma: Use physical_memory_map() for mem2mem transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The soc_dma code has a fastpath for when DMA transfers are from RAM to RAM. The current implementation of this has the caller of soc_dma_port_add_mem() pass the underlying host address of the RAM block that the DMA port is connected to (obtained via memory_region_get_ram_ptr()). Then the actual transfer function does a simple memcpy(). This has several problems. Most importantly, no bounds checking is done on the address and size passed by the guest, so the memcpy source and destination might be outside the backing host RAM entirely. Secondly, because the DMA access is done via this back door, there is no updating of the dirty region when memory is written this way (there is a TODO comment in omap_dma.c noting this). Fix both of these by making the memory to memory transfer function use physical_memory_map() to get the host addresses for the memory copy. That function will automatically give us the bounds check that we want and return a short length if the transfer would run off the end of the RAM MemoryRegion it starts in. Since the OMAP DMA documentation states that it's a guest error to misprogram the addresses so that they fall outside the range that is valid for the particular DMA port being addressed and that this can result in guest memory corruption , we don't need to loop for short transfers, but can simply log them and continue. Note that we don't need to update addresses or bytecount here in the transfer function, because when soc_dma_ch_update() selects transfer_mem2mem it also sets ch->update to 1, which tells the omap_dma_transfer_setup() code that it is responsible for updating all the guest visible fields to match "transfer completed". (We use physical_memory_map() here to match the use of physical_memory_read() and physical_memory_write() in omap_dma.c; making the DMA controller use an explicit AddressSpace would be a separate cleanup task.) Together with the preceding commits that fixed some integer overflow problems, this fixes the "guest can provoke a bad memcpy() operation" reported in issue #3204. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3204 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260710105907.2570621-7-peter.maydell@linaro.org --- hw/dma/omap_dma.c | 5 ----- hw/dma/soc_dma.c | 51 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 14b310bf40..1a3ae74726 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -559,11 +559,6 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) if (!ch->sync && frames) { ch->cpc = a->dest & 0xffff; } - - /* - * TODO: if the destination port is IMIF or EMIFF, set the dirty - * bits on it. - */ } omap_dma_interrupts_update(s); diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 1b93aaf565..6e325122d9 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -20,13 +20,58 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "qemu/log.h" +#include "system/physmem.h" #include "hw/arm/soc_dma.h" static void transfer_mem2mem(struct soc_dma_ch_s *ch) { - memcpy(ch->paddr[0], ch->paddr[1], ch->bytes); - ch->paddr[0] += ch->bytes; - ch->paddr[1] += ch->bytes; + /* + * Memory-to-memory transfer: do the whole thing in one go. The + * hardware spec says that it is invalid to program the OMAP DMA + * controller with addresses that don't match the port (i.e. to + * ask for a transfer to/from a memory port with a physaddr that + * isn't within that port range) and that if you do then the + * transfer continues and memory can be corrupted. So we can map + * both source and destination, and treat short mappings and + * failed mappings as a guest error. + */ + hwaddr srclen = ch->bytes; + hwaddr dstlen = ch->bytes; + hwaddr srcaddr = ch->vaddr[0]; + hwaddr dstaddr = ch->vaddr[1]; + void *srcmem, *dstmem; + hwaddr xferlen = 0; + + srcmem = physical_memory_map(srcaddr, &srclen, false); + if (!srcmem) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not map source; " + "guest error programming source port/address\n"); + return; + } + + dstmem = physical_memory_map(dstaddr, &dstlen, true); + if (!dstmem) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not map destination; " + "guest error programming destination port/address\n"); + goto unmap_src; + } + + xferlen = MIN(srclen, dstlen); + if (xferlen < ch->bytes) { + qemu_log_mask(LOG_GUEST_ERROR, + "soc_dma mem2mem transfer: could not transfer all data; " + "guest error programming src or destination addresses\n"); + /* Continue to transfer whatever did fit in the port window */ + } + + memmove(dstmem, srcmem, xferlen); + + physical_memory_unmap(dstmem, dstlen, true, xferlen); +unmap_src: + physical_memory_unmap(srcmem, srclen, false, xferlen); } struct dma_s { From f5541f51cf0782485c3e77e05f1a5adc73b1d692 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 072/860] hw/dma/soc_dma: Remove unused mem.base, paddr fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that transfer_mem2mem() uses physical_memory_map(), the soc_dma_ch_s::paddr field is unused; remove it, and the code that set it, and the memmap_entry_s::mem.base and the soc_dma_port_add_mem() phys_base argument that were passing around host pointers to use for setting paddr. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260710105907.2570621-8-peter.maydell@linaro.org --- hw/arm/omap1.c | 6 ++---- hw/dma/soc_dma.c | 9 +-------- include/hw/arm/soc_dma.h | 7 ++----- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 44f9dd67c3..9e739a0712 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -3799,10 +3799,8 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, s->port[tipb_mpui].addr_valid = omap_validate_tipb_mpui_addr; /* Register SDRAM and SRAM DMA ports for fast transfers. */ - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(dram), - OMAP_EMIFF_BASE, s->sdram_size); - soc_dma_port_add_mem(s->dma, memory_region_get_ram_ptr(&s->imif_ram), - OMAP_IMIF_BASE, s->sram_size); + soc_dma_port_add_mem(s->dma, OMAP_EMIFF_BASE, s->sdram_size); + soc_dma_port_add_mem(s->dma, OMAP_IMIF_BASE, s->sram_size); s->timer[0] = omap_mpu_timer_init(system_memory, 0xfffec500, qdev_get_gpio_in(s->ih[0], OMAP_INT_TIMER1), diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 6e325122d9..ff890149ee 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -85,7 +85,6 @@ struct dma_s { enum soc_dma_port_type type; hwaddr addr; struct { - void *base; size_t size; } mem; } *memmap; @@ -154,10 +153,6 @@ static inline enum soc_dma_port_type soc_dma_ch_update_type( if (ch->type[port] != soc_dma_access_const) return soc_dma_port_other; - ch->paddr[port] = (uint8_t *) entry->mem.base + - (ch->vaddr[port] - entry->addr); - /* TODO: save bytes left to the end of the mapping somewhere so we - * can check we're not reading beyond it. */ return soc_dma_port_mem; } else return soc_dma_port_other; @@ -245,8 +240,7 @@ struct soc_dma_s *soc_dma_init(int n) return &s->soc; } -void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, - hwaddr virt_base, size_t size) +void soc_dma_port_add_mem(struct soc_dma_s *soc, hwaddr virt_base, size_t size) { struct memmap_entry_s *entry; struct dma_s *dma = (struct dma_s *) soc; @@ -293,7 +287,6 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, entry->addr = virt_base; entry->type = soc_dma_port_mem; - entry->mem.base = phys_base; entry->mem.size = size; } diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index b5ad9743be..fdae7a29c2 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -53,8 +53,6 @@ struct soc_dma_ch_s { /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ enum soc_dma_access_type type[2]; hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ - /* Private */ - void *paddr[2]; int running; soc_dma_transfer_t transfer_fn; @@ -83,7 +81,6 @@ void soc_dma_set_request(struct soc_dma_ch_s *ch, int level); * calling soc_dma_set_request(ch, 1): * ch->type[0...1], * ch->vaddr[0...1], - * ch->paddr[0...1], * or after a soc_dma_port_add_mem(). */ void soc_dma_ch_update(struct soc_dma_ch_s *ch); @@ -92,7 +89,7 @@ void soc_dma_ch_update(struct soc_dma_ch_s *ch); void soc_dma_reset(struct soc_dma_s *s); struct soc_dma_s *soc_dma_init(int n); -void soc_dma_port_add_mem(struct soc_dma_s *dma, uint8_t *phys_base, - hwaddr virt_base, size_t size); +void soc_dma_port_add_mem(struct soc_dma_s *dma, + hwaddr virt_base, size_t size); #endif From a5223ea5364b6e24dd2e85a98db55ff6f2c34dc5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 073/860] include/hw/arm/omap_dma.h: Move to include/hw/dma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit omap_dma.h is the header file for hw/dma/omap_dma.c; it fits better to put it in include/hw/dma/ to match where we have the .c file. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260710105907.2570621-9-peter.maydell@linaro.org --- hw/arm/omap1.c | 2 +- hw/dma/omap_dma.c | 2 +- hw/dma/soc_dma.c | 2 +- include/hw/{arm => dma}/soc_dma.h | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename include/hw/{arm => dma}/soc_dma.h (100%) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 9e739a0712..0f78cbd410 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -32,7 +32,7 @@ #include "hw/sd/sd.h" #include "system/blockdev.h" #include "system/system.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" #include "system/qtest.h" #include "system/reset.h" #include "system/runstate.h" diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 1a3ae74726..16575e73b7 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -22,7 +22,7 @@ #include "qemu/timer.h" #include "hw/arm/omap.h" #include "hw/core/irq.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" #include "system/physmem.h" struct omap_dma_channel_s { diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index ff890149ee..4feb22e6ea 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -22,7 +22,7 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "system/physmem.h" -#include "hw/arm/soc_dma.h" +#include "hw/dma/soc_dma.h" static void transfer_mem2mem(struct soc_dma_ch_s *ch) { diff --git a/include/hw/arm/soc_dma.h b/include/hw/dma/soc_dma.h similarity index 100% rename from include/hw/arm/soc_dma.h rename to include/hw/dma/soc_dma.h From fcebaea2e3fc71ad191349b65f937016d8ee0506 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 074/860] MAINTAINERS: Add soc_dma to OMAP section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hw/dma/soc_dma.c code appears to have been written with the idea that it abstracts out DMA transfer operations from the details of a particular DMA controller device. In practice, it's used only by the omap_dma code and I would not today recommend trying to use it in any new DMA device. Add the files to the OMAP section of MAINTAINERS so that patches can be cc'd to the appropriate places. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260710105907.2570621-10-peter.maydell@linaro.org --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 74fc43f5f2..a28935c898 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2205,7 +2205,9 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/omap* +F: hw/dma/soc_dma.c F: include/hw/arm/omap.h +F: include/hw/dma/soc_dma.h F: docs/system/arm/sx1.rst F: tests/functional/arm/test_sx1.py From b693da615a7ab2adae7383fecb87b0e13966256e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 20 Jul 2026 19:05:30 +0100 Subject: [PATCH 075/860] Revert "target/arm: Build cpu32-system.o as common object" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In c8bea1276c we moved cpu32.c and built it as a common object. The commit message says "cpu32.c only contains CPU types used in 32-bit system emulation". However, this is incorrect -- it contains 32-bit CPU types used in both system and usermode emulation. (The case where we don't need these CPUs and which we were using ifdefs to avoid is specifically the AArch64 usermode qemu-aarch64 binary.) The effect is that qemu-arm lost all the named CPU types except the M-profile ones (which are in cpu-v7m.c). This reverts commit c8bea1276c85855b2e088b740737d043c8556d58. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3962 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-id: 20260720100312.119894-1-peter.maydell@linaro.org --- target/arm/tcg/{cpu32-system.c => cpu32.c} | 10 +++++++++- target/arm/tcg/meson.build | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) rename target/arm/tcg/{cpu32-system.c => cpu32.c} (99%) diff --git a/target/arm/tcg/cpu32-system.c b/target/arm/tcg/cpu32.c similarity index 99% rename from target/arm/tcg/cpu32-system.c rename to target/arm/tcg/cpu32.c index 6e98390089..8220d785f5 100644 --- a/target/arm/tcg/cpu32-system.c +++ b/target/arm/tcg/cpu32.c @@ -1,5 +1,5 @@ /* - * QEMU ARM TCG-only CPUs (not needed for the AArch64 linux-user build) + * QEMU ARM TCG-only CPUs. * * Copyright (c) 2012 SUSE LINUX Products GmbH * @@ -13,9 +13,15 @@ #include "cpu.h" #include "accel/tcg/cpu-ops.h" #include "internals.h" +#if !defined(CONFIG_USER_ONLY) #include "hw/core/boards.h" +#endif #include "cpregs.h" + +/* CPU models. These are not needed for the AArch64 linux-user build. */ +#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) + static void arm926_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -740,3 +746,5 @@ static void arm_tcg_cpu_register_types(void) } type_init(arm_tcg_cpu_register_types) + +#endif /* !CONFIG_USER_ONLY || !TARGET_AARCH64 */ diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 573e8b5af4..227e674ae2 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -30,6 +30,10 @@ translate32_d = [ arm_ss.add(when: 'TARGET_AARCH64', if_true: gen_a64) arm_stubs_ss.add(files('stubs32.c')) +arm_ss.add(files( + 'cpu32.c', +)) + arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'gengvec64.c', 'translate-a64.c', @@ -82,7 +86,6 @@ arm_common_user_system_ss.add(when: 'TARGET_AARCH64', if_true: files( arm_common_system_ss.add(files( 'cpregs-at.c', - 'cpu32-system.c', 'gicv5-cpuif.c', 'psci.c', 'tlb_helper.c', From bc5b22b4d465fcd9ba708889876dcfe66bef3deb Mon Sep 17 00:00:00 2001 From: Ziyang Zhang Date: Sun, 19 Jul 2026 15:47:28 +0800 Subject: [PATCH 076/860] contrib/plugins/dlcall: correct the syscall number claim, note the data model The comment claimed every Linux ABI keeps its syscall numbers well below the minimum, and that the minimum is all N has to satisfy. Neither holds. MIPS O32 bases its numbering at 4000, so the default 4096 is its getpriority. Raising N does not rescue it either, because O32 answers numbers its table does not define with ENOSYS before the filter runs, which leaves no number that is both free and reachable on that ABI. arm32 bounds N from above too, with ENOSYS or SIGILL past ARM_NR_BASE. Say all of this, so the number can be chosen with the target in mind. guest_base == 0 is not the only requirement either. Host pointers are written back through the caller's out pointers, so the guest must match the host's pointer width and endianness. Fold that into the existing warning. Also assert the two out pointers that lacked it, and point at Lorelei for argument marshalling, callbacks and variadic functions. Co-authored-by: Kailiang Xu Co-authored-by: Mingyuan Xia Signed-off-by: Ziyang Zhang Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260719074730.1520517-2-functioner@sjtu.edu.cn Signed-off-by: Pierrick Bouvier --- contrib/plugins/dlcall.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/contrib/plugins/dlcall.c b/contrib/plugins/dlcall.c index 9d2230b2d1..b624735e62 100644 --- a/contrib/plugins/dlcall.c +++ b/contrib/plugins/dlcall.c @@ -10,7 +10,8 @@ * nothing about how a library is thunked. Any toolchain can implement the * userspace side. Lorelei is one end-to-end implementation (guest/host * runtimes plus a thunk compiler that generates thunks from a library's - * headers): + * headers), and how it handles argument marshalling, callbacks and variadic + * functions can serve as a reference: * https://github.com/rover2024/lorelei * * See docs/about/emulation.rst|Dynamic Linking Call for details and examples. @@ -20,12 +21,13 @@ * execution in the QEMU host process. It is NOT a sandbox and provides no * isolation; only load it for guests you fully trust. * - * WARNING: requires guest_base == 0, which is qemu-user's default. Pointer - * operands are dereferenced as host addresses directly, and the invoked host - * functions dereference guest pointers with no address translation, so guest - * and host must share a single address space. A non-zero guest_base (e.g. set - * via -B/-R) would make every pointer off by guest_base and hit unrelated - * host memory. + * WARNING: requires guest_base == 0, which is qemu-user's default, and a + * guest whose pointer width and endianness match the host's. Pointer operands + * are dereferenced as host addresses directly, and the invoked host functions + * dereference guest pointers with no address translation, so guest and host + * must share a single address space and agree on how a pointer is stored. A + * non-zero guest_base (e.g. set via -B/-R) would make every pointer off by + * guest_base and hit unrelated host memory. * * SPDX-License-Identifier: GPL-2.0-or-later */ @@ -46,8 +48,18 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; * * It defaults to DLCALL_SYSCALL_DEFAULT and can be overridden at load time * with the "syscall_num=N" argument. To avoid hijacking a real syscall the - * guest might issue, N must be at least DLCALL_SYSCALL_MIN: every Linux ABI - * keeps its syscall numbers well below this; numbers from here up are free. + * guest might issue, N must be at least DLCALL_SYSCALL_MIN, which most Linux + * ABIs keep their syscall numbers well below. + * + * N also has to reach the filter at all, which bounds it from above in a + * target specific way: arm32 answers anything past ARM_NR_BASE (0xf0000) with + * ENOSYS or SIGILL before do_syscall() runs, while aarch64 has no such bound. + * + * MIPS O32 bases its numbering at 4000, so the default is a real syscall there + * (getpriority). Raising N does not help either, because O32 rejects numbers + * its table does not define, again before the filter runs, which leaves no + * number that is both free and reachable on that ABI. Its N32 and N64 ABIs + * base at 6000 and 5000 and have no such gate, so they are unaffected. */ enum { DLCALL_SYSCALL_DEFAULT = 4096, @@ -168,6 +180,7 @@ static bool vcpu_syscall_filter(unsigned int vcpu_index, case DLCALL_ID_FREE_LIBRARY: { void *handle = (void *) a2; int *ret_ptr = (int *) a3; + assert(ret_ptr); *ret_ptr = dlclose(handle); *sysret = 0; break; @@ -176,6 +189,7 @@ static bool vcpu_syscall_filter(unsigned int vcpu_index, /* Get the last error message for a library event. */ case DLCALL_ID_GET_LIBRARY_ERROR: { const char **error_ptr = (const char **) a2; + assert(error_ptr); *error_ptr = dlerror(); *sysret = 0; break; From 99916cdb4dbf8459b6d908eea907ae18294e1635 Mon Sep 17 00:00:00 2001 From: Ziyang Zhang Date: Sun, 19 Jul 2026 15:47:29 +0800 Subject: [PATCH 077/860] tests/tcg: correct why the magic syscall number is safe here The comment said 4096 was picked because no ISA in Linux uses it. The same comment already notes that mips 32 bits numbers from 4000, which makes 4096 its getpriority. What actually keeps this test safe is the filter, which matches on the first argument as well, so a real syscall carrying this number is left alone. Say that instead. Co-authored-by: Kailiang Xu Co-authored-by: Mingyuan Xia Signed-off-by: Ziyang Zhang Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260719074730.1520517-3-functioner@sjtu.edu.cn Signed-off-by: Pierrick Bouvier --- tests/tcg/multiarch/test-plugin-syscall-filter.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/tcg/multiarch/test-plugin-syscall-filter.c b/tests/tcg/multiarch/test-plugin-syscall-filter.c index 951e338a7c..089fb2a110 100644 --- a/tests/tcg/multiarch/test-plugin-syscall-filter.c +++ b/tests/tcg/multiarch/test-plugin-syscall-filter.c @@ -23,9 +23,12 @@ int main(int argc, char *argv[]) * "linux-user/arm/cpu_loop.c:cpu_loop". * As well, some arch expect a minimum, like 4000 for mips 32 bits. * - * Therefore, we pick 4096 because, as of now, no ISA in Linux uses this - * number. This is just a test case; replace this number as needed in the - * future. + * Therefore, we pick 4096, which sits between those bounds. It is not + * unused everywhere though: mips 32 bits numbers from 4000, so 4096 is its + * getpriority. This test is unaffected because the filter also requires + * the first argument to be 0x66CCFF, so a real syscall carrying this + * number falls through untouched. This is just a test case, so replace + * this number as needed in the future. * * The corresponding syscall filter is implemented in * "tests/tcg/plugins/syscall.c". From 533c98e769c79bcef56c6e466c4f3cb4c195181e Mon Sep 17 00:00:00 2001 From: Ziyang Zhang Date: Sun, 19 Jul 2026 15:47:30 +0800 Subject: [PATCH 078/860] docs/about/emulation: sharpen the dlcall boundary and its guest requirements Record the same data model requirement as the plugin: guest_base == 0 is necessary but not sufficient. Describe the magic syscall number the way the plugin now does. It has to be a number the guest ABI does not use and does not reject before the plugin sees it, rather than merely a high one, so show syscall_num= being used as well. A library is not turned into thunks, it is left alone and the thunks are produced for it, so say that instead. Argument marshalling, callbacks and variadic functions are also what the plugin does not do, and listing them in its description blurs the boundary it draws. Move them to Lorelei, where they are pointed at as a reference. Co-authored-by: Kailiang Xu Co-authored-by: Mingyuan Xia Signed-off-by: Ziyang Zhang Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260719074730.1520517-4-functioner@sjtu.edu.cn Signed-off-by: Pierrick Bouvier --- docs/about/emulation.rst | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index b861501e85..c58149086c 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -1072,14 +1072,13 @@ syscall, so the real kernel never sees it. Trusted guests only. The guest can load arbitrary host libraries and run arbitrary code in the QEMU host process. The plugin is not a sandbox and provides no isolation. It also requires ``guest_base == 0`` (qemu-user's - default), as guest pointers are dereferenced as host addresses with no - translation. + default) and a guest whose pointer width and endianness match the host's, as + guest pointers are dereferenced as host addresses with no translation. The plugin intentionally keeps the QEMU side lightweight and knows nothing -about any particular library or its calling convention. Turning a real library -into working thunks, including argument marshalling, callbacks and variadic -functions, is done entirely in userspace, and any toolchain can implement the -interface. +about any particular library or its calling convention. Producing the thunks +for a real library is done entirely in userspace, and any toolchain can +implement the interface. Loading the plugin is all that is required from QEMU's side: @@ -1087,11 +1086,21 @@ Loading the plugin is all that is required from QEMU's side: qemu-x86_64 -plugin contrib/plugins/libdlcall.so ... +If the default number does not suit the guest ABI, pick another one, and build +the userspace side to issue the same one: + +.. code-block:: shell + + qemu-x86_64 -plugin contrib/plugins/libdlcall.so,syscall_num=8192 \ + ... + `Lorelei `_ is one end-to-end userspace implementation of this: it provides the guest and host runtimes and an automated toolchain that generates the thunks from a library's headers, so guest -library calls run on the host's native libraries. It supports an x86_64 guest -running on an x86_64, aarch64 or riscv64 host. +library calls run on the host's native libraries. How it handles the parts the +plugin leaves out, including argument marshalling, callbacks and variadic +functions, can serve as a reference. It supports an x86_64 guest running on an +x86_64, aarch64 or riscv64 host. A minimal end-to-end example uses a one-function library, ``libhello.so``, built two ways: the guest build tags its output ``(from the guest)`` and the host @@ -1201,8 +1210,10 @@ which prints:: * - Option - Description * - syscall_num=N - - The magic syscall number the guest issues (default 4096). Must be high - enough not to clash with a real syscall. + - The magic syscall number the guest issues (default 4096). It must be a + number the guest ABI does not use for a real syscall, and does not + reject before the plugin sees it, which bounds the choice from both + sides. Other emulation features ------------------------ From b1e73b2ddbb2850e394b9c47cfcb14dba8048a71 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 29 Jun 2026 17:42:46 +0100 Subject: [PATCH 079/860] net: Correct padding check in qemu_receive_packet() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In qemu_receive_packet() we check to see if we should pad a short packet. This is doing the wrong test: because this function is used when the device adds a packet to its own incoming queue (i.e. for loopback), we should be checking the NetClientState's own do_not_pad flag, not that for its peer. We didn't notice this earlier, because at the moment all the real peers of a network device (i.e. the network backends) do not set do_not_pad, so net_peer_needs_padding() always returns true except in the corner case where the network device has no peer at all. The effect of this is that if a network device has no peer (e.g. because QEMU was started with -net none or with -nodefaults) then we can still let through the kind of "guest misprograms the network device to loopback-transmit a short packet and then we mishandle it in the receive path" bug like #3043 which commit a01344d9d78 was trying to fix. Since the distinction between "we should check nc->do_not_pad" and "we should check nc->peer->do_not_pad" is a bit subtle, add enough documentation commentary to make it more obvious. Cc: qemu-stable@nongnu.org Fixes: a01344d9d78 ("net: pad packets to minimum length in qemu_receive_packet()") Suggested-by: Bin Meng Signed-off-by: Peter Maydell Reviewed-by: Bin Meng Message-ID: <20260629164246.2028947-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/net/net.h | 23 +++++++++++++++++++++++ net/net.c | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/net/net.h b/include/net/net.h index 45bc86fc86..9edfacf827 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -349,9 +349,32 @@ uint32_t net_crc32_le(const uint8_t *p, int len); .offset = vmstate_offset_macaddr(_state, _field), \ } +/** + * net_peer_needs_padding: Should we pad as we send out packets? + * @nc: NetClientState + * + * Return true if the peer of this NetClientState (i.e. the + * destination that qemu_send_packet() etc send to) requires us to pad + * out packets that are shorter than the minimum ethernet frame + * length. + */ static inline bool net_peer_needs_padding(NetClientState *nc) { return nc->peer && !nc->peer->do_not_pad; } +/** + * net_client_needs_padding: Should we pad as we queue packets to ourselves? + * @nc: NetClientState + * + * Return true if this NetClientState requires us to pad out packets + * that are shorter than the minimum ethernet frame length. This is + * the check to make in qemu_receive_packet() when we are queuing a + * packet back into ourselves (i.e. loopback). + */ +static inline bool net_client_needs_padding(NetClientState *nc) +{ + return !nc->do_not_pad; +} + #endif diff --git a/net/net.c b/net/net.c index 5c39f8e7b4..0a30579ca4 100644 --- a/net/net.c +++ b/net/net.c @@ -783,7 +783,7 @@ ssize_t qemu_receive_packet(NetClientState *nc, const uint8_t *buf, int size) return 0; } - if (net_peer_needs_padding(nc)) { + if (net_client_needs_padding(nc)) { if (eth_pad_short_frame(min_pkt, &min_pktsz, buf, size)) { buf = min_pkt; size = min_pktsz; From 9f436938c52125324e36cf9c13b877aa8aada096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 15 Jul 2026 11:27:22 +0400 Subject: [PATCH 080/860] hw/display/qxl: validate monitors_config heads[] in phys2virt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qxl_phys2virt() call for guest_monitors_config only validates sizeof(QXLMonitorsConfig), which covers the fixed header (count and max_allowed) since commit 8efec0ef8bbc ("hw/display/qxl: Pass requested buffer size to qxl_phys2virt()"), but not the flexible array member heads[]. When count == 1, heads[0] is accessed without its memory being validated, allowing a guest to cause an out-of-bounds read. Include sizeof(QXLHead) in the size passed to qxl_phys2virt() so that the first head entry is validated within the guest memory slot, preventing guest-visible memory reading. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4027 Reported-by: Tristan @TristanInSec Signed-off-by: Marc-Andre Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260715072722.1643289-1-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/qxl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 74258afa58..3df796175c 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -270,7 +270,7 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) } cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST, - sizeof(QXLMonitorsConfig)); + sizeof(QXLMonitorsConfig) + sizeof(QXLHead)); if (cfg != NULL && cfg->count == 1) { qxl->guest_primary.resized = 1; qxl->guest_head0_width = cfg->heads[0].width; From 701e8d1ee92033632868d80283a5a383641c3375 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Jul 2026 16:22:48 -0700 Subject: [PATCH 081/860] user/guest-host: Include exec/abi_ptr.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The COMPILING_PER_TARGET block uses the abi_ptr type without including the proper header. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260717232306.378988-2-richard.henderson@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/user/guest-host.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/user/guest-host.h b/include/user/guest-host.h index 506efc097e..f136f81f5d 100644 --- a/include/user/guest-host.h +++ b/include/user/guest-host.h @@ -80,6 +80,7 @@ static inline bool guest_range_valid_untagged_vaddr(vaddr start, vaddr len) }) #ifdef COMPILING_PER_TARGET +#include "exec/abi_ptr.h" /* * These functions take the guest virtual address as an abi_ptr. This From fe68e4b47bf5a3ff016a8c2b580e05c93df05752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 19 Jul 2026 15:32:16 +0400 Subject: [PATCH 082/860] replay: fix use of uninitialized pointer on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When bdrv_snapshot_list() returns a negative error code, sn_tab is uninitialized. The loop does not execute (since i=0 < negative is false), but the code falls through to g_free(sn_tab) which frees an uninitialized pointer. Fixes: f6baed3d1485 ("replay: implement replay-seek command") Signed-off-by: Marc-Andre Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260719113216.1177594-1-marcandre.lureau@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- replay/replay-debugging.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/replay/replay-debugging.c b/replay/replay-debugging.c index 1105364002..7b904a1416 100644 --- a/replay/replay-debugging.c +++ b/replay/replay-debugging.c @@ -139,9 +139,8 @@ static char *replay_find_nearest_snapshot(int64_t icount, int64_t *snapshot_icount) { BlockDriverState *bs; - QEMUSnapshotInfo *sn_tab; + g_autofree QEMUSnapshotInfo *sn_tab = NULL; QEMUSnapshotInfo *nearest = NULL; - char *ret = NULL; int rv; int nb_sns, i; @@ -149,15 +148,19 @@ static char *replay_find_nearest_snapshot(int64_t icount, bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL); if (!bs) { - goto fail; + return NULL; } nb_sns = bdrv_snapshot_list(bs, &sn_tab); + if (nb_sns < 0) { + return NULL; + } for (i = 0; i < nb_sns; i++) { rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL); - if (rv < 0) - goto fail; + if (rv < 0) { + return NULL; + } if (rv == 1) { if (sn_tab[i].icount != -1ULL && sn_tab[i].icount <= icount @@ -166,14 +169,12 @@ static char *replay_find_nearest_snapshot(int64_t icount, } } } - if (nearest) { - ret = g_strdup(nearest->name); - *snapshot_icount = nearest->icount; + if (!nearest) { + return NULL; } - g_free(sn_tab); -fail: - return ret; + *snapshot_icount = nearest->icount; + return g_strdup(nearest->name); } static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp) From 2142689d92ca87b53ad1a0a8d48f439baf17e13a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 20 Jul 2026 10:06:04 +0200 Subject: [PATCH 083/860] hw/misc/applesmc: Fix a typo setting MSSD key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 1ddda5cd364 we meant to set MSSD=3, but due to a typo we ended setting MSSD=0. Convert the two other NATJ and MSSP keys to use hexadecimal notation to avoid similar copy/paste typos. Cc: qemu-stable@nongnu.org Fixes: 1ddda5cd364 ("AppleSMC device emulation") Reported-by: Matthew Jackson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alexander Graf Reviewed-by: Pierrick Bouvier Message-Id: <20260720115336.75063-1-philmd@oss.qualcomm.com> --- hw/misc/applesmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index fd96f5f245..d004e8b443 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -333,9 +333,9 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp) applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); applesmc_add_key(s, "OSK0", 32, s->osk); applesmc_add_key(s, "OSK1", 32, s->osk + 32); - applesmc_add_key(s, "NATJ", 1, "\0"); - applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); + applesmc_add_key(s, "NATJ", 1, "\x00"); + applesmc_add_key(s, "MSSP", 1, "\x00"); + applesmc_add_key(s, "MSSD", 1, "\x03"); } static void applesmc_unrealize(DeviceState *dev) From 94c510a5dc4836550af1ecddcfa1ff945bae562a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Mon, 20 Jul 2026 20:07:31 +0800 Subject: [PATCH 084/860] hw/net/cadence: Return current Cadence GEM queue pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cadence GEM queue pointer registers are programmed with the descriptor ring base, but reads return the descriptor currently being accessed. The model tracked the current positions separately while continuing to return the configured base. The Linux macb driver uses the transmit queue pointer when recovering from a used-buffer interrupt. A stale priority-queue pointer can make the driver restart DMA before that queue handles its completion interrupt, causing queue 0 to repeatedly raise TX_USED. The primary queue has had this mismatch since the initial model. Priority queue support later copied the same register-read behavior. A single-queue machine usually handles TX_COMPLETE before TX_USED and empties the software queue before the restart check, which kept the issue hidden there. Return the current RX and TX descriptor positions on queue-pointer reads and clear those positions on reset. Fixes: e9f186e514a7 ("cadence_gem: initial version of device model") Fixes: 6710172501be ("cadence_gem: Add queue support") Cc: qemu-stable@nongnu.org Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260720120731.2022475-1-bin.meng@processmission.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/cadence_gem.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index b568fa3392..39e3620ef5 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1469,6 +1469,8 @@ static void gem_reset(DeviceState *d) /* Set post reset register values */ memset(&s->regs[0], 0, sizeof(s->regs)); + memset(&s->rx_desc_addr[0], 0, sizeof(s->rx_desc_addr)); + memset(&s->tx_desc_addr[0], 0, sizeof(s->tx_desc_addr)); s->regs[R_NWCFG] = 0x00080000; s->regs[R_NWSTATUS] = 0x00000006; s->regs[R_DMACFG] = 0x00020784; @@ -1593,9 +1595,22 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) offset >>= 2; retval = s->regs[offset]; - DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset*4, retval); + DB_PRINT("offset: 0x%04x read: 0x%08x\n", (unsigned)offset * 4, + retval); switch (offset) { + case R_RXQBASE: + retval = s->rx_desc_addr[0]; + break; + case R_TXQBASE: + retval = s->tx_desc_addr[0]; + break; + case R_TRANSMIT_Q1_PTR ... R_TRANSMIT_Q7_PTR: + retval = s->tx_desc_addr[offset - R_TRANSMIT_Q1_PTR + 1]; + break; + case R_RECEIVE_Q1_PTR ... R_RECEIVE_Q7_PTR: + retval = s->rx_desc_addr[offset - R_RECEIVE_Q1_PTR + 1]; + break; case R_ISR: DB_PRINT("lowering irqs on ISR read\n"); /* The interrupts get updated at the end of the function. */ From c477e32b645df6f72fe64f59615fd2195faa64c8 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 20 Jul 2026 22:11:32 +0200 Subject: [PATCH 085/860] hw/sd/sdcard: Fix error case for CMD18 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 468fa450a7e0 ("hw/sd: Switch read/write primitive to buf+len"), `sd_read_byte()` changed its contract to return the read size rather than the read value (and was renamed to `sd_read_data()` accordingly). In an error case, however, `sd_read_data()` returns 0 by means of `dummy_byte` which is the code for the old contract. Moreover, `sdbus_read_data()` asserts the virtual method `read_data()` (and thus `sd_read_data()`) to return a non-zero size, i.e. to make progress and not loop forever. Fix the code to behave like the "DAT read illegal for command" case. Fixes: 468fa450a7e0 ("hw/sd: Switch read/write primitive to buf+len") Reviewed-by: Bin Meng Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260720201133.24796-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 3360757004..a30c541df0 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2860,7 +2860,8 @@ static size_t sd_read_data(SDState *sd, void *buf, size_t length) if (sd->data_offset == 0) { if (!address_in_range(sd, "READ_MULTIPLE_BLOCK", sd->data_start, io_len)) { - return dummy_byte; + *value = dummy_byte; + return length; } partition_access = sd->ext_csd[EXT_CSD_PART_CONFIG] & EXT_CSD_PART_CONFIG_ACC_MASK; From c257745248b02fd468619b4b1fdbbade5344191d Mon Sep 17 00:00:00 2001 From: Haotian Jiang Date: Tue, 21 Jul 2026 14:09:41 +0800 Subject: [PATCH 086/860] hw/audio/intel-hda: restrict all DMA engine paths to memories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE-2021-3611 (commit 79fa99831d) restricted the DMA engine to memories by setting attrs.memory=true, but only applied this to intel_hda_response. Three other DMA engine access points still use MEMTXATTRS_UNSPECIFIED, allowing a malicious guest to trigger DMA-to-self-MMIO reentry: - intel_hda_xfer (line 398): called from the audio timer callback (hda_codec_xfer -> bus->xfer), so the MemReentrancyGuard does not fire (engaged_in_io is false outside MMIO dispatch). A guest that points a BDL entry at the HDA controller's own MMIO BAR can write audio samples to device registers, triggering whandler side effects such as starting/stopping streams or injecting codec commands via CORBWP. - intel_hda_parse_bdl (line 478): uses pci_dma_read which hardcodes MEMTXATTRS_UNSPECIFIED. A guest-controlled BDL base address can point at controller MMIO, allowing the DMA engine to read device registers as BDL descriptors. - intel_hda_corb_run (line 333): ldl_le_pci_dma reads the CORB ring with MEMTXATTRS_UNSPECIFIED, allowing the DMA engine to read controller MMIO as CORB entries. Fix all three by passing {.memory = true} explicitly, matching the fix already applied to intel_hda_response. For intel_hda_parse_bdl, replace pci_dma_read with pci_dma_rw to pass the controlled attrs. Fixes: 79fa99831d ("hw/audio/intel-hda: Restrict DMA engine to memories (not MMIO devices)") Reported-by: Haotian Jiang of Tencent Security (Yunding Lab) Signed-off-by: Haotian Jiang Cc: qemu-stable@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260721060941.2989396-1-jianghaotian.sunday@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/audio/intel-hda.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index d7c2c3c2fd..3d361a4976 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -305,6 +305,7 @@ static int intel_hda_send_command(IntelHDAState *d, uint32_t verb) static void intel_hda_corb_run(IntelHDAState *d) { + const MemTxAttrs attrs = { .memory = true }; hwaddr addr; uint32_t rp, verb; @@ -330,7 +331,7 @@ static void intel_hda_corb_run(IntelHDAState *d) rp = (d->corb_rp + 1) & 0xff; addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); - ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, MEMTXATTRS_UNSPECIFIED); + ldl_le_pci_dma(&d->pci, addr + 4 * rp, &verb, attrs); d->corb_rp = rp; dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __func__, rp, verb); @@ -395,7 +396,7 @@ static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t res static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, uint8_t *buf, uint32_t len) { - const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED; + const MemTxAttrs attrs = { .memory = true }; HDACodecBus *bus = HDA_BUS(dev->qdev.parent_bus); IntelHDAState *d = container_of(bus, IntelHDAState, codecs); hwaddr addr; @@ -466,6 +467,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) { + const MemTxAttrs attrs = { .memory = true }; hwaddr addr; uint8_t buf[16]; uint32_t i; @@ -475,7 +477,8 @@ static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) g_free(st->bpl); st->bpl = g_new(bpl, st->bentries); for (i = 0; i < st->bentries; i++, addr += 16) { - pci_dma_read(&d->pci, addr, buf, 16); + pci_dma_rw(&d->pci, addr, buf, 16, + DMA_DIRECTION_TO_DEVICE, attrs); st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf); st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8)); st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12)); From d06edc83b522c68627b83d3178197353c4d548ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 21 Jul 2026 08:27:45 +0200 Subject: [PATCH 087/860] vfio/listener: Remove unnecessary 'linux/kvm.h' include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit d0e8bccafc23 ("hw/vfio/listener.c: remove CONFIG_KVM"), the linux/kvm.h include is unconditional. This breaks the build on targets that lack asm/kvm.h such as sparc and sparc64: In file included from ../hw/vfio/listener.c:23: linux-headers/linux/kvm.h:16:10: fatal error: asm/kvm.h: No such file or directory This include is not needed in listener.c which only uses kvm_enabled() and kvm_get_max_memslots(), both are declared in "system/kvm.h". Remove it. Fixes: d0e8bccafc23 ("hw/vfio/listener.c: remove CONFIG_KVM") Cc: Pierrick Bouvier " Suggested-by: Alex Williamson Signed-off-by: Cédric Le Goater Tested-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260721062745.3793066-1-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/vfio/listener.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index c19600e980..008f488a3e 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include -#include #include #include "exec/target_page.h" From ad14439be5731e872d8448a04664a9c0e66624a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 20 Jul 2026 19:04:59 +0100 Subject: [PATCH 088/860] gitlab: remove gdb from MacOS jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gdb jobs fail on CI and although I can't replicate locally on the machine I have access to it definitely fixes it for the gitlab CI machines. Signed-off-by: Alex Bennée Reviewed-by: Pierrick Bouvier Message-ID: <20260720180459.2542918-1-alex.bennee@linaro.org> [This fixes the following CI jobs: aarch64-macos-15-build and aarch64-macos-26-build. Removing gdb from macOS jobs skips the softmmu gdbstub tests which currently hang on GitLab's macOS runners: qemu-system-aarch64: -gdb unix:path=/var/folders/fc/8_hzw5tx3n7c9c04tzd6tv_40000gn/T/tmpes3paikrqemu-gdbstub/gdbstub.socket,server=on: info: QEMU waiting for connection on: disconnected:unix:/var/folders/fc/8_hzw5tx3n7c9c04tzd6tv_40000gn/T/tmpes3paikrqemu-gdbstub/gdbstub.socket,server=on gmake[1]: *** [/Users/gitlab/builds/qemu-project/qemu/tests/tcg/multiarch/system/Makefile.softmmu-target:35: run-gdbstub-interrupt] Error 137 gmake: *** [/Users/gitlab/builds/qemu-project/qemu/tests/Makefile.include:75: run-tcg-tests-aarch64-softmmu] Error 2 The following GitLab work item tracks solving the underlying issue that is hanging softmmu gdbstub tests: https://gitlab.com/qemu-project/qemu/-/work_items/4063 --Stefan] Signed-off-by: Stefan Hajnoczi --- .gitlab-ci.d/macos.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/macos.yml b/.gitlab-ci.d/macos.yml index 53b6e6c4d8..641a48b383 100644 --- a/.gitlab-ci.d/macos.yml +++ b/.gitlab-ci.d/macos.yml @@ -22,7 +22,7 @@ - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH" - brew update - brew install $PKGS - - brew install gdb aarch64-elf-gcc i686-elf-gcc x86_64-elf-gcc + - brew install aarch64-elf-gcc i686-elf-gcc x86_64-elf-gcc - if test -n "$PYPI_PKGS" ; then PYLIB=$($PYTHON -c 'import sysconfig; print(sysconfig.get_path("stdlib"))'); rm -f $PYLIB/EXTERNALLY-MANAGED; $PIP3 install --break-system-packages $PYPI_PKGS ; fi script: - mkdir build From 7430c34b25a8d6f08281c64cc01438e0ec3c91ab Mon Sep 17 00:00:00 2001 From: Jessica Clarke Date: Thu, 9 Jul 2026 19:42:27 +0100 Subject: [PATCH 089/860] test-util-filemonitor: Adapt to FreeBSD 15's native inotify semantics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FreeBSD 15 introduces a native inotify implementation rather than requiring use of the kqueue-based libinotify package. This native implementation does not generate the extra deleted events, so don't expect them. However, the original implementation did have a bug that caused IN_IGNORED to never be generated if you did not also watch for IN_DELETE_SELF, which affects 15.0 and 15.1, but has been fixed and will no longer apply in 15.2 / 16.0. Note that the deleted event check is for the userspace version, since that governs whether libinotify is being used or not, whereas the ignored event check is both for the userspace version (to check if we're using the native syscall) and the kernel version (to check if the kernel has the bug or not). All __FreeBSD_version values used here correspond to the value in-tree at the time of the relevant commits. Since neither commit bumped the value there will be a window of development snapshots between each commit and the previous bump that will be incorrectly identified here, but this is the best we can do, and something users of snapshots should be prepared to deal with. Reviewed-by: Daniel P. Berrangé Signed-off-by: Jessica Clarke Signed-off-by: Daniel P. Berrangé --- tests/unit/test-util-filemonitor.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/unit/test-util-filemonitor.c b/tests/unit/test-util-filemonitor.c index 02e67fc96a..972651dc1e 100644 --- a/tests/unit/test-util-filemonitor.c +++ b/tests/unit/test-util-filemonitor.c @@ -27,6 +27,10 @@ #include +#ifdef __FreeBSD__ +#include +#endif + enum { QFILE_MONITOR_TEST_OP_ADD_WATCH, QFILE_MONITOR_TEST_OP_DEL_WATCH, @@ -221,6 +225,24 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data, } +static bool +expect_broken_ignored(void) +{ +#if defined(__FreeBSD__) && __FreeBSD_version >= 1500051 + int osreldate; + + osreldate = getosreldate(); + if (osreldate == -1) { + g_printerr("Unable to call getosreldate: %s\n", strerror(errno)); + abort(); + } + return osreldate < 1501501 || (osreldate >= 1600000 && osreldate < 1600019); +#else + return false; +#endif +} + + static void test_file_monitor_events(void) { @@ -360,7 +382,7 @@ test_file_monitor_events(void) { .type = QFILE_MONITOR_TEST_OP_EVENT, .filesrc = "one.txt", .watchid = &watch4, .eventid = QFILE_MONITOR_EVENT_DELETED }, -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) && __FreeBSD_version < 1500051 { .type = QFILE_MONITOR_TEST_OP_EVENT, .filesrc = "two.txt", .watchid = &watch0, .eventid = QFILE_MONITOR_EVENT_DELETED }, @@ -539,6 +561,11 @@ test_file_monitor_events(void) g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", *op->watchid, op->eventid, op->filesrc); } + if (op->eventid == QFILE_MONITOR_EVENT_IGNORED && + expect_broken_ignored()) { + g_printerr("Expect ignored event to be broken, skipping\n"); + break; + } if (!qemu_file_monitor_test_expect(&data, *op->watchid, op->eventid, op->filesrc, op->swapnext)) From 1b03da3bf88e6f23b455150ca0f48dc0d29790e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 10 Jul 2026 11:56:39 +0100 Subject: [PATCH 090/860] tests/vm: update to FreeBSD 14.4 image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids an interactive prompt that halts execution when attempting to run the FreeBSD tests due to outdated 14.3 base image: `Bootstrapping pkg from pkg+https://pkg.FreeBSD.org/FreeBSD:14:amd64/quarterly, please wait... Verifying signature with trusted certificate pkg.freebsd.org.2013102301... done Installing pkg-2.6.2_1... Newer FreeBSD version for package pkg: To ignore this error set IGNORE_OSVERSION=yes - package: 1404000 - running userland: 1403000 Ignore the mismatch and continue? [y/N]: Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- tests/vm/freebsd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/vm/freebsd b/tests/vm/freebsd index ea09b21fbc..bfb1a85af3 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,8 +28,8 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/releases/CI-IMAGES/14.3-RELEASE/amd64/Latest/FreeBSD-14.3-RELEASE-amd64-BASIC-CI.raw.xz" - csum = "ec0f5a4bbe63aa50a725d9fee0f1931f850e9a21cbebdadb991df00f168d6805" + link = "https://download.freebsd.org/releases/CI-IMAGES/14.4-RELEASE/amd64/Latest/FreeBSD-14.4-RELEASE-amd64-BASIC-CI.raw.xz" + csum = "627937af554eb6522891875ddc8cf5b86b40e9a12143c1b951f830b3f691de49" size = "20G" BUILD_SCRIPT = """ From 15ebb1ec8c06360c0fff0719b880ad55cdfe4db2 Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Fri, 10 Jul 2026 15:25:30 +0200 Subject: [PATCH 091/860] meson.build: re-add explicit gcrypt/nettle request check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit c4b3d0074 removed the check that nettle or gcrypt were explicitly requested as the crypto library to use, breaking the --enable-nettle and --enable-gcrypt options. Re-add the logic to force usage of nettle or gcrypt for crypto operations, while still keeping gnutls for TLS. Fixes: c4b3d0074 (crypto: bump min gnutls to 3.7.5) Tested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Luc Michel Signed-off-by: Daniel P. Berrangé --- crypto/cipher.c | 2 +- crypto/meson.build | 2 +- meson.build | 11 ++++++++++- tests/unit/test-crypto-block.c | 3 ++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/crypto/cipher.c b/crypto/cipher.c index 515165e0dc..229710f76b 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -142,7 +142,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgo alg, #include "cipher-gcrypt.c.inc" #elif defined CONFIG_NETTLE #include "cipher-nettle.c.inc" -#elif defined CONFIG_GNUTLS +#elif defined CONFIG_GNUTLS_CRYPTO #include "cipher-gnutls.c.inc" #else #include "cipher-stub.c.inc" diff --git a/crypto/meson.build b/crypto/meson.build index b51597a879..6ac83857aa 100644 --- a/crypto/meson.build +++ b/crypto/meson.build @@ -38,7 +38,7 @@ if nettle.found() endif elif gcrypt.found() crypto_ss.add(gcrypt, files('hash-gcrypt.c', 'hmac-gcrypt.c', 'pbkdf-gcrypt.c')) -elif gnutls.found() +elif gnutls_crypto.found() crypto_ss.add(gnutls, files('hash-gnutls.c', 'hmac-gnutls.c', 'pbkdf-gnutls.c')) else crypto_ss.add(files('hash-glib.c', 'hmac-glib.c', 'pbkdf-stub.c')) diff --git a/meson.build b/meson.build index 164328ded8..06c43b4358 100644 --- a/meson.build +++ b/meson.build @@ -1783,11 +1783,13 @@ if not get_option('libcbor').auto() or have_system 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) gnutls = dependency('gnutls', version: '>=3.7.5', method: 'pkg-config', required: get_option('gnutls')) + gnutls_crypto = gnutls #if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() # XXX: when bug 1717 is resolved, add logic to probe for @@ -1811,7 +1813,12 @@ if get_option('nettle').enabled() and get_option('gcrypt').enabled() error('Only one of gcrypt & nettle can be enabled') endif -if not gnutls.found() +# Explicit nettle/gcrypt request, so ignore gnutls for crypto +if get_option('nettle').enabled() or get_option('gcrypt').enabled() + gnutls_crypto = not_found +endif + +if not gnutls_crypto.found() if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled() gcrypt = dependency('libgcrypt', version: '>=1.9.4', required: get_option('gcrypt')) @@ -2513,6 +2520,7 @@ config_host_data.set('CONFIG_XKBCOMMON', xkbcommon.found()) 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()) @@ -4865,6 +4873,7 @@ summary_info = {} 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} diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index 218e585f98..3ac7f17b2a 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -31,7 +31,8 @@ #endif #if (defined(_WIN32) || defined RUSAGE_THREAD) && \ - (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) + (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT) || \ + defined(CONFIG_GNUTLS_CRYPTO)) #define TEST_LUKS #else #undef TEST_LUKS From 4f7615789bb1594335f23d404dff51b2841dfe96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 24 Jun 2026 10:35:14 +0100 Subject: [PATCH 092/860] gitlab: introduce files mapping GitLab accounts to real names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is desirable to be able to discover the GitLab account handle assocaited with a real name in the MAINTAINERS file. Rather that duplicating the same account handle multiple times, inline with the MAINTAINERS file entries, this introduces mapping files: * .gitlab-map-auto - data automatically queried from GitLab using the 'glab' tool and REST API * .gitlab-map-manual - manual overrides/augmentation for cases where the MAINTAINERS real name does not match the GitLab account real name The former would need refreshing when we add new MAINTAINERS entries, if the person had to be added as a GitLab account member. For this purpose scripts/gitlab-map-update can be used, assuming the user has the 'glab' client tool present and configured with an access token. To audit how many maintainers have GitLab handles present/missing scripts/gitlab-map-check can run a report. $ ./scripts/gitlab-map-check Missing GitLab handle for maintainer 'Akihiko Odaki' Missing GitLab handle for maintainer 'Albert Esteve' .... Missing GitLab handle for maintainer 'Zhenzhong Duan' Missing GitLab handle for maintainer 'Zhuoying Cai' GitLab handles missing: 158 / present: 68 Reviewed-by: Fabiano Rosas Signed-off-by: Daniel P. Berrangé --- .gitlab-map-auto | 93 +++++++++++++++++++++++++++++++++++++++ .gitlab-map-manual | 18 ++++++++ scripts/gitlab-map-check | 51 +++++++++++++++++++++ scripts/gitlab-map-update | 45 +++++++++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 .gitlab-map-auto create mode 100644 .gitlab-map-manual create mode 100755 scripts/gitlab-map-check create mode 100755 scripts/gitlab-map-update diff --git a/.gitlab-map-auto b/.gitlab-map-auto new file mode 100644 index 0000000000..28ce448f1b --- /dev/null +++ b/.gitlab-map-auto @@ -0,0 +1,93 @@ +# This file is auto-generated by scripts/gitlab-map-update +# +# This GitLab map associates GitLab account handles +# with real names, in order to allow mapping from +# MAINTAINERS entries. The format of entries is +# +# {gitlab-handle}{real name} +# +# Manual overrides must be placed in .gitlab-map-manual +TaoTang Tao Tang +a1xndr Alexander Bulekov +adi-g15-ibm Aditya Gupta +agraf Alexander Graf +alex.williamson Alex Williamson +aliang1 Aihua Liang +alistair23 Alistair Francis +anisinha Ani Sinha +anthony-linaro Anthony Roberts +anthonyper Anthony PERARD +berrange Daniel P. Berrangé +birkelund Klaus Jensen +bonzini Paolo Bonzini +brian-cain Brian Cain +bsdimp Warner Losh +cborntra Christian Borntraeger +chao23.liu Chao Liu (Zevorn) +cleber.gnu Cleber Rosa +clegoate Cédric Le Goater +cohuck Cornelia Huck +cota_ Emilio Cota +dagrh Dr. David Alan Gilbert +danielhb Daniel Henrique Barboza +davidhildenbrand David Hildenbrand +dgibson dgibson +dwmw2 David Woodhouse +eauger1 Eric Auger +ebblake Eric Blake +edgar.iglesias Edgar E. Iglesias +ehabkost Eduardo Habkost +eldondev Eldon +epilys Manos Pitsidianakis +famzheng Fam Zheng +farosas Fabiano Rosas +gautammenghani Gautam Menghani +gkurz Greg Kurz +gusbromero Gustavo Romero +harshpb Harsh Prateek Bora +hdeller Helge Deller +hreitz Hanna Czenczek +imammedo Igor Mammedov +jasowang Jason Wang +jmacarthur Jim MacArthur +jsnow John Snow +juan.quintela Juan Quintela +kbastian-qemu Bastian Koppelmann +kmwolf Kevin Wolf +kostyanf14 Kostiantyn Kostiuk +kraxel Gerd Hoffmann +lbmeng Bin Meng +legoater Cédric Le Goater +lvivier Laurent Vivier +lygstate Yonggang Luo +maciejsszmigiero Maciej S. Szmigiero +marcandre.lureau Marc-André Lureau +marcandre.lureau-rh Marc-André Lureau +mauromatteo.cascella Mauro Matteo Cascella +mcayland Mark Cave-Ayland +mdroth Michael Roth +mediouni-m M. Mediouni +mjt0k Michael Tokarev +mstredhat MST +npiggin npiggin +p-b-o Pierrick Bouvier +pauldzim Paul Zimmerman +peterx Peter Xu +philmd Philippe Mathieu-Daudé +pierrick.bouvier Pierrick Bouvier +pipo.sk Peter Krempa +pkrempa Peter Krempa (work) +pm215 Peter Maydell +qemu-janitor Qemu Janitor +rathc Chinmay Rath +rth7680 Richard Henderson +schoenebeck Christian Schoenebeck +sgarzarella Stefano Garzarella +sstabellini Stefano Stabellini +stefanberger Stefan Berger +stefanha Stefan Hajnoczi +stsquad Alex Bennée +stweil Stefan Weil +thuth Thomas Huth +vsementsov Vladimir Sementsov-Ogievskiy +xcancerberox Joaquin de Andres diff --git a/.gitlab-map-manual b/.gitlab-map-manual new file mode 100644 index 0000000000..f605a540e9 --- /dev/null +++ b/.gitlab-map-manual @@ -0,0 +1,18 @@ +# This GitLab map associates GitLab account handles +# with real names, in order to allow mapping from +# MAINTAINERS entries. The format of entries is +# +# {gitlab-handle}{real name} +# +# This file is manually written, to augment the +# auto-generated data in .gitlab-map-auto. This +# is needed where a GitLab account real name does +# not exactly match the MAINTAINERS file real +# name. +# +berrange Daniel P. Berrange +dgibson David Gibson +hreitz Hanna Reitz +mstredhat Michael S. Tsirkin +npiggin Nicholas Piggin +mediouni-m Mohamed Mediouni diff --git a/scripts/gitlab-map-check b/scripts/gitlab-map-check new file mode 100755 index 0000000000..f72a06756d --- /dev/null +++ b/scripts/gitlab-map-check @@ -0,0 +1,51 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +missing=0 +present=0 + +grep -E '^(R|M):' MAINTAINERS | \ +sed -E -e 's/^(M|R): //' -e 's/ <.*//' | \ +sort | uniq > names.txt +while IFS= read -r NAME +do + grep "$NAME" .gitlab-map-{auto,manual} > /dev/null + if test $? != 0 + then + echo "Missing GitLab handle for maintainer '$NAME'" + missing=$(($missing + 1)) + else + present=$(($present + 1)) + fi +done < names.txt +rm -f names.txt + +echo "GitLab handles missing: $missing / present: $present" + +# .gitlab-map-manual is to provide alternate real names +# for cases where name in MAINTAINERS does not match +# the GitLab account real name. As such... + +# ...all gitlab handles in .gitlab-map-manual must also exist +# in .gitlab-map-auto and .... +for HANDLE in $(grep -v '^#' .gitlab-map-manual | awk '{print $1}') +do + grep -E "^$HANDLE\s" .gitlab-map-auto >/dev/null + if test $? != 0 + then + echo "Unexpected GitLab handle '$HANDLE' is not a member" + fi +done + +# ...and all realnames in .gitlab-map-manual must also +# exist in MAINTAINERS +grep -v '^#' .gitlab-map-manual | \ +awk '{ $1 = ""; print substr($0, 2) }' | \ +while IFS= read -r NAME +do + grep -E "\s$NAME\s" MAINTAINERS >/dev/null + if test $? != 0 + then + echo "Unexpected real name '$NAME' is not a maintainer" + fi +done diff --git a/scripts/gitlab-map-update b/scripts/gitlab-map-update new file mode 100755 index 0000000000..c134c226aa --- /dev/null +++ b/scripts/gitlab-map-update @@ -0,0 +1,45 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later + +echo "Checking authentication token status" +glab auth status + +if test $? != 0 +then + echo "ERROR: not authenticated with gitlab.com" + exit 1 +fi + +if ! test -f .gitlab-map-auto +then + echo "ERROR: cannot find existing '.gitlab-map-auto file'" + echo "ERROR: this must be executed from the root of the" + echo "ERROR: git repository checkout" + exit 1 +fi + +fail=0 +echo "Updating member list from qemu-project" +glab api --paginate /groups/qemu-project/members | \ + jq -r -c '.[] | [.username, .name] | @tsv' | \ + grep -v '^group_3038080_bot' > \ + .gitlab-map-auto.tmp +test $? != 0 && fail=1 + +echo "Updating member list from qemu-project/qemu" +glab api --paginate /projects/qemu-project%2fqemu/members | \ + jq -r -c '.[] | [.username, .name] | @tsv' | \ + grep -v 'Duo Developer' >> \ + .gitlab-map-auto.tmp +test $? != 0 && fail=1 + +if test $fail == 0 +then + grep '^#' .gitlab-map-auto > .gitlab-map-auto.new + LC_ALL=C sort .gitlab-map-auto.tmp | uniq >> .gitlab-map-auto.new + mv .gitlab-map-auto.new .gitlab-map-auto + echo "OK: GitLab member list updated" +else + echo "ERROR: Updating member list failed" +fi +rm -f .gitlab-map-auto.tmp From 84a6c5f94b3d3c445c55189d38aca22234054c10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 22 Jun 2026 11:59:50 +0100 Subject: [PATCH 093/860] get_maintainer: add ability to report Git Lab handle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the GitLab mapping files from the previous commit, the get_manitainer.pl script is now able to report the gitlab handle for each maintainer/reviewer when displaying output. For example: $ ./scripts/get_maintainer.pl -f hw/scsi/lsi53c895a.c Paolo Bonzini (supporter:SCSI, gitlab:@bonzini) Fam Zheng (reviewer:SCSI, gitlab:@famzheng) qemu-devel@nongnu.org (open list:All patches CC here) Reviewed-by: Fabiano Rosas Signed-off-by: Daniel P. Berrangé --- scripts/get_maintainer.pl | 53 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index 76be402e11..473d133126 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -365,6 +365,31 @@ sub read_mailmap { close($mailmap_file); } +my %gitlabmap; + +read_gitlabmap(".gitlab-map-auto"); +read_gitlabmap(".gitlab-map-manual"); + +sub read_gitlabmap { + my $mapfile = shift; + open MAP, "<$mapfile" + or die "cannot open $mapfile: $!"; + + while () { + next if /^#/; + next if /^\s*$/; + + if (/^(\S+)\t(.*)$/) { + my $handle = $1; + my $realname = $2; + + $gitlabmap{$realname} = $handle; + } + } + + close MAP; +} + ## use the filenames on the command line or find the filenames in the patchfiles my @files = (); @@ -1068,6 +1093,18 @@ sub email_inuse { return 0; } +sub gitlab_handle { + my $name = shift; + + $name =~ s/"//g; + + my $gitlab_handle; + if (exists $gitlabmap{$name}) { + return $gitlabmap{$name}; + } + return undef; +} + sub push_email_address { my ($line, $role) = @_; @@ -1077,10 +1114,14 @@ sub push_email_address { return 0; } + my $gitlab_handle = gitlab_handle($name); + if (!$email_remove_duplicates) { - push(@email_to, [format_email($name, $address, $email_usename), $role]); + push(@email_to, [format_email($name, $address, $email_usename), + $role, $gitlab_handle]); } elsif (!email_inuse($name, $address)) { - push(@email_to, [format_email($name, $address, $email_usename), $role]); + push(@email_to, [format_email($name, $address, $email_usename), + $role, $gitlab_handle]); $email_hash_name{lc($name)}++ if ($name ne ""); $email_hash_address{lc($address)}++; } @@ -2026,10 +2067,14 @@ sub merge_email { my %saw; for (@_) { - my ($address, $role) = @$_; + my ($address, $role, $gitlab_handle) = @$_; if (!$saw{$address}) { if ($output_roles) { - push(@lines, "$address ($role)"); + if (defined $gitlab_handle) { + push(@lines, "$address ($role, gitlab:\@$gitlab_handle)"); + } else { + push(@lines, "$address ($role)"); + } } else { push(@lines, $address); } From 256229e8f9b950a41b01ecee30524e7bd3235ef7 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Wed, 8 Jul 2026 18:31:00 +0800 Subject: [PATCH 094/860] vfio/igd: Clear saved BDSM in legacy VBIOS ROM at load time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IGD does not come with a ROM BAR [1], the ROM BAR read by default from kernel is actually the host VBIOS shadow RAM region that contains host modifications on boot. With AI-assisted reverse engineering on VBIOS binaries, it is observed that VBIOS saves BDSM register value on first access and uses saved value if present. When the image is executed in guest, since there is already a saved HPA in VBIOS, it keeps using that value instead of the GPA programmed by SeaBIOS in BDSM register in PCI config space, causing VBIOS to program GTT entries with wrong address, resulting in garbled output in BIOS POST and the error below detected by i915 driver. i915 0000:00:02.0: [drm] *ERROR* Initial plane programming using invalid range, dma_addr=0x00000000db200000 ((null) [0x00000000baf00000-0x00000000beefffff]) The previous solution, c4c45e943e51 ("vfio/pci: Intel graphics legacy mode assignment"), adjusts GTT entry addresses to (addr - host BDSM + guest BDSM) to workaround that. But it is removed in 5aed8b0f0be2 ("vfio/igd: Remove GTT write quirk in IO BAR 4") due to inconsistent values in MMIO BAR0 and IO BAR4. Since it was a value latched into the VBIOS that breaks virtualization (QEMU does not map the GTT at the same address in the VM), a ROM quirk clearing the saved value in VBIOS image is introduced. It searches the BDSM accessor routine by matching a 19-byte signature anchored on the unique `mov $0x105e,%ax` instruction, then locates the offset of saved BDSM and clears it. This makes the routine fall through to the PCI config read on the first call inside the guest. [1] 3.5.15, 4th Generation Intel Core Processor Family Datasheet Vol. 2 https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/4th-gen-core-family-desktop-vol-2-datasheet.pdf Fixes: 5aed8b0f0be2 ("vfio/igd: Remove GTT write quirk in IO BAR 4") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3093 Reported-by: K S Maan Cc: qemu-stable@nongnu.org Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20260708103100.23127-1-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd-stubs.c | 5 ++ hw/vfio/igd.c | 115 +++++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci-quirks.c | 5 ++ hw/vfio/pci.c | 2 + hw/vfio/pci.h | 3 ++ hw/vfio/trace-events | 1 + 6 files changed, 131 insertions(+) diff --git a/hw/vfio/igd-stubs.c b/hw/vfio/igd-stubs.c index f7687d9091..5f60b24c8b 100644 --- a/hw/vfio/igd-stubs.c +++ b/hw/vfio/igd-stubs.c @@ -18,3 +18,8 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) { return true; } + +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev) +{ + return; +} diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index e091f21b6a..413a49aae9 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -724,3 +724,118 @@ bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) return vfio_pci_igd_config_quirk(vdev, errp); } + +/* + * IGD ROM BAR read from kernel is actually the host VBIOS shadow RAM region, + * which contains host modifications. In Gen 6-9 VBIOS, the routine below is + * used to get BDSM value when programming the initial GTT. + * xx xx xx xx v: .long ? # saved value + * 66 53 push %ebx + * 66 2e 83 3e xx xx 00 cmpl $0x0,%cs:v # is saved value empty? + * 74 07 je 1f # if zero, go compute + * 66 2e a1 xx xx mov %cs:v,%eax # else return saved value + * eb 0f jmp 2f + * b8 5e 10 1: mov $0x105e,%ax # dev 00:02.0, offset 5E + * e8 xx xx call pci_read_cfg_word + * 66 c1 e0 10 shl $0x10,%eax # left shift 16 bits + * 66 2e a3 xx xx mov %eax,%cs:v # save the result + * 66 5b 2: pop %ebx + * c3 ret + * When running the VBIOS in guest, saved value still reflects the host stolen + * memory base address, which is not correct in guest. So we need to patch the + * VBIOS to clear the saved value. + * + * The unique 19-byte starts at `cmpl $0,%cs:v` and ends at `mov $0x105e,%ax` + * anchors the match to the routine. Both `cs:` displacements must reference + * the same offset. + */ +static int igd_vbios_find_saved_bdsm(const uint8_t *rom, size_t rom_size, + uint16_t *bdsm_offset) +{ + static const uint8_t start[] = { 0x66, 0x2e, 0x83, 0x3e }; + static const uint8_t middle[] = { 0x00, 0x74, 0x07, 0x66, 0x2e, 0xa1 }; + static const uint8_t end[] = { 0xeb, 0x0f, 0xb8, 0x5e, 0x10 }; + uint16_t val; + size_t i; + bool found = false; + + if (rom_size < 19) { + return -ENOENT; + } + + for (i = 0; i + 19 <= rom_size; i++) { + if (memcmp(rom + i, start, sizeof(start)) != 0 || + memcmp(rom + i + 6, middle, sizeof(middle)) != 0 || + memcmp(rom + i + 14, end, sizeof(end)) != 0) { + continue; + } + + /* same saved value address? */ + if (rom[i + 4] != rom[i + 12] || rom[i + 5] != rom[i + 13]) { + continue; + } + + if (found) { + return -EEXIST; + } + + val = rom[i + 4] | ((uint16_t)rom[i + 5] << 8); + if (val + sizeof(uint32_t) <= rom_size) { + *bdsm_offset = val; + found = true; + } + } + + if (!found) { + return -ENOENT; + } + + return 0; +} + +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev) +{ + uint8_t *rom = vdev->rom; + int gen; + uint16_t pcir_offset; + uint16_t bdsm_offset = 0; + uint8_t checksum = 0; + uint32_t i; + + if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || + !vfio_is_vga(vdev) || !vdev->vga) { + return; + } + + /* Only Gen 6~9 devices have legacy VBIOS as Option ROM */ + gen = igd_gen(vdev); + if (gen < 6 || gen > 9) { + return; + } + + if (pci_get_word(rom) != 0xaa55) { + return; + } + + /* Must be a legacy ROM */ + pcir_offset = pci_get_word(rom + 0x18); + if (pcir_offset + 0x14 >= vdev->rom_size || + memcmp(rom + pcir_offset, "PCIR", 4) || + pci_get_byte(rom + pcir_offset + 0x14) != 0x00) { + return; + } + + /* Search and clear the saved BDSM value */ + if (igd_vbios_find_saved_bdsm(rom, vdev->rom_size, &bdsm_offset)) { + return; + } + memset(rom + bdsm_offset, 0, sizeof(uint32_t)); + + /* Recalculate checksum and patch it. */ + for (i = 0; i < vdev->rom_size; i++) { + checksum += rom[i]; + } + rom[6] -= checksum; + + trace_vfio_pci_igd_vbios_patched(vdev->vbasedev.name); +} diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index bccf31751f..c5b4f9091d 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1592,3 +1592,8 @@ bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp) return true; } + +void vfio_rom_quirk_setup(VFIOPCIDevice *vdev) +{ + vfio_igd_legacy_rom_quirk(vdev); +} diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c204706e63..dcfc92aae1 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -1119,6 +1119,8 @@ static bool vfio_pci_load_rom(VFIOPCIDevice *vdev, Error **errp) } } + vfio_rom_quirk_setup(vdev); + return true; } diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index fe52e9df6e..c9ab949870 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -252,10 +252,13 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); bool vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); +void vfio_rom_quirk_setup(VFIOPCIDevice *vdev); void vfio_quirk_reset(VFIOPCIDevice *vdev); VFIOQuirk *vfio_quirk_alloc(int nr_mem); + void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr); bool vfio_probe_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp); +void vfio_igd_legacy_rom_quirk(VFIOPCIDevice *vdev); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index f71d0bbc0a..bfdaf229e4 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -90,6 +90,7 @@ vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_ vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" vfio_pci_igd_host_bridge_enabled(const char *name) "%s" vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" +vfio_pci_igd_vbios_patched(const char *name) "%s" # listener.c vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64 From 4b2e6505d59b7b16c6e15a2494942d00648e8510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 15 Jul 2026 10:00:00 +0200 Subject: [PATCH 095/860] vfio/region: Clarify dma-buf failure messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dma-buf failure messages in vfio_region_create_dma_buf() say "PCI BAR IOMMU mappings may fail", which suggests the BAR is broken. In practice, only P2P DMA is affected -- normal passthrough uses the mmap fallback. Reword both messages to mention P2P DMA explicitly and clarify that the mmap fallback is in use. Use warn_report_err_once() at the call site so per-BAR repetition on mdev devices is suppressed. Fixes: dcf1b77e834d ("hw/vfio/region: Create dmabuf for PCI BAR per region") Cc: Nicolin Chen Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- hw/vfio/region.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/vfio/region.c b/hw/vfio/region.c index dbde339180..54ad11a6c8 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -321,13 +321,12 @@ static bool vfio_region_create_dma_buf(VFIORegion *region, Error **errp) ret = vfio_device_get_feature(vbasedev, feature); if (ret < 0) { if (ret == -ENOTTY) { - warn_report_once("VFIO dma-buf not supported in kernel: " - "PCI BAR IOMMU mappings may fail"); + warn_report_once("VFIO dma-buf not supported in kernel, " + "using mmap fallback, P2P DMA will not work"); return true; } - /* P2P DMA or exposing device memory use cases are not supported. */ - error_setg_errno(errp, -ret, "%s: failed to create dma-buf: " - "PCI BAR IOMMU mappings may fail", + error_setg_errno(errp, -ret, "%s: dma-buf unavailable, " + "using mmap fallback, P2P DMA will not work", memory_region_name(region->mem)); return false; } @@ -448,7 +447,7 @@ int vfio_region_mmap(VFIORegion *region) } if (!vfio_region_create_dma_buf(region, &local_err)) { - error_report_err(local_err); + warn_report_err_once(local_err); } return 0; From dff528657fc114119ece1478ad845b0292a87683 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 17 Jul 2026 14:22:29 +0200 Subject: [PATCH 096/860] vfio/pci: don't narrow a failed config read to a plausible value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_pci_read_config() signals a failed host-side read by returning (uint32_t)-1, regardless of the requested length. vfio_intx_enable() and vfio_pci_pre_reset() both narrowed that return value straight into a uint8_t/uint16_t local before checking anything, which truncates -1 into 0xff or 0xffff - values a real 1- or 2-byte register read can legitimately produce. From that point on, a failed read and real all-ones content are indistinguishable. Keep the full uint32_t result and check it against (uint32_t)-1 before narrowing. In vfio_pci_pre_reset(), skip the corresponding write-back on a failed read instead of writing back constructed garbage to the device. Resolves: Coverity CID 1663684 Resolves: Coverity CID 1663688 Signed-off-by: Denis V. Lunev CC: Alex Williamson CC: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260717122232.468955-2-den@openvz.org [ clg: Added Coverity IDs ] Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index dcfc92aae1..b8c937d4be 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -323,10 +323,16 @@ static void vfio_irqchip_change(Notifier *notify, void *data) static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(vdev); - uint8_t pin = vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1); + uint32_t val = vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1); + uint8_t pin; Error *err = NULL; int32_t fd; + if (val == (uint32_t)-1) { + error_setg(errp, "failed to read PCI_INTERRUPT_PIN"); + return false; + } + pin = val; if (!pin) { return true; @@ -2766,6 +2772,7 @@ bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp) void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { PCIDevice *pdev = PCI_DEVICE(vdev); + uint32_t val; uint16_t cmd; vfio_disable_interrupts(vdev); @@ -2774,23 +2781,34 @@ void vfio_pci_pre_reset(VFIOPCIDevice *vdev) * Stop any ongoing DMA by disconnecting I/O, MMIO, and bus master. * Also put INTx Disable in known state. */ - cmd = vfio_pci_read_config(pdev, PCI_COMMAND, 2); - cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | - PCI_COMMAND_INTX_DISABLE); - vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + val = vfio_pci_read_config(pdev, PCI_COMMAND, 2); + if (val != (uint32_t)-1) { + cmd = val; + cmd &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE); + vfio_pci_write_config(pdev, PCI_COMMAND, cmd, 2); + } /* Make sure the device is in D0 */ if (pdev->pm_cap) { uint16_t pmcsr; uint8_t state; - pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + val = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + if (val == (uint32_t)-1) { + return; + } + pmcsr = val; state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { pmcsr &= ~PCI_PM_CTRL_STATE_MASK; vfio_pci_write_config(pdev, pdev->pm_cap + PCI_PM_CTRL, pmcsr, 2); /* vfio handles the necessary delay here */ - pmcsr = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + val = vfio_pci_read_config(pdev, pdev->pm_cap + PCI_PM_CTRL, 2); + if (val == (uint32_t)-1) { + return; + } + pmcsr = val; state = pmcsr & PCI_PM_CTRL_STATE_MASK; if (state) { error_report("vfio: Unable to power on device, stuck in D%d", From 86abb10256a58d50513d1f9c65652b7b6b7faafc Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 17 Jul 2026 14:22:30 +0200 Subject: [PATCH 097/860] vfio/pci: reject invalid PCI_INTERRUPT_PIN values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu-kvm aborts a few seconds after starting a VM with a passed-through GPU whose PCI_INTERRUPT_PIN comes back as an out-of-range value: vfio_intx_enable() only guards against pin == 0 and stores vdev->intx.pin = pin - 1 with no upper-bound check. That value later reaches pci_irq_handler()'s assert(0 <= irq_num && irq_num < PCI_NUM_PINS) via pci_irq_deassert() -> pci_set_irq(), aborting the process. Legal PCI_INTERRUPT_PIN values are 0 (no legacy interrupt) or 1-PCI_NUM_PINS (INTA-INTD); reject anything else before it reaches vdev->intx.pin, whether the out-of-range value came from a read failure (now caught by the previous commit) or was handed back as data by the device itself. Signed-off-by: Denis V. Lunev CC: Alex Williamson CC: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260717122232.468955-3-den@openvz.org Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b8c937d4be..380dd8c15f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -338,6 +338,11 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) return true; } + if (pin > PCI_NUM_PINS) { + error_setg(errp, "invalid PCI interrupt pin %d", pin); + return false; + } + /* * Do not alter interrupt state during vfio_realize and cpr load. * The incoming state is cleared thereafter. From f6a321c94b21f4c4e65de96078d04bd62df08b23 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:26:51 +0000 Subject: [PATCH 098/860] vfio-user: vfio_user_get_region_info: prevent buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the vfio-user responds with a value large enough such that adding the header size to it overflows, a smaller buffer would be inadvertently allocated, leading to buffer overflow. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3867 Signed-off-by: Thanos Makatos Fixes: 667866d66620 ("vfio-user: implement VFIO_USER_DEVICE_GET_REGION_INFO") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-2-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index b8d2b7c1a8..e58167a818 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -128,12 +128,21 @@ static int vfio_user_get_region_info(VFIOUserProxy *proxy, error_printf("vfio_user_get_region_info argsz too small\n"); return -E2BIG; } + + /* + * Ensure that size doesn't overflow, otherwise we'll allocate a much + * smaller buffer than we need. + */ + if (__builtin_add_overflow(info->argsz, sizeof(VFIOUserHdr), &size)) { + error_printf("vfio_user_get_region_info argsz too large\n"); + return -E2BIG; + } + if (fds != NULL && fds->send_fds != 0) { error_printf("vfio_user_get_region_info can't send FDs\n"); return -EINVAL; } - size = info->argsz + sizeof(VFIOUserHdr); msgp = g_malloc0(size); vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO, From 152f7dd3b04f1ad8e99248610c74f876e0da2107 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:26:52 +0000 Subject: [PATCH 099/860] vfio-user: vfio_user_get_region_info: respect max_xfer_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thanos Makatos Fixes: 667866d66620 ("vfio-user: implement VFIO_USER_DEVICE_GET_REGION_INFO") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-3-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index e58167a818..8350bb2c01 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -137,6 +137,10 @@ static int vfio_user_get_region_info(VFIOUserProxy *proxy, error_printf("vfio_user_get_region_info argsz too large\n"); return -E2BIG; } + if (size > proxy->max_xfer_size) { + error_printf("vfio_user_get_region_info argsz too large\n"); + return -E2BIG; + } if (fds != NULL && fds->send_fds != 0) { error_printf("vfio_user_get_region_info can't send FDs\n"); From 3966a4258b597c7e1a86a65d6a0d15f28a09dca8 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:26:53 +0000 Subject: [PATCH 100/860] vfio-user: vfio_user_get_region_info: reject unreasonably short struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While this isn't technically a bug, it's highly unlikely that the server wouldn't be writing an entire struct. Signed-off-by: Thanos Makatos Fixes: 667866d66620 ("vfio-user: implement VFIO_USER_DEVICE_GET_REGION_INFO") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-4-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 8350bb2c01..3d12e8b2a2 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -165,6 +165,11 @@ static int vfio_user_get_region_info(VFIOUserProxy *proxy, } trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size); + if (msgp->argsz < sizeof(*info)) { + error_printf("vfio_user_get_region_info reply argsz too small\n"); + return -EINVAL; + } + memcpy(info, &msgp->argsz, info->argsz); /* From 6a51aab908e0f043387b91ad6a198b0eae5f2b58 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:26:55 +0000 Subject: [PATCH 101/860] vfio-user: vfio_user_get_region_info: prevent excessive malloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the vfio-user server responds with a value larger than max_xfer_size vfio_device_get_region_info() blindly uses it in the next loop in g_realloc. An value larger than max_xfer_size is anyway rejected by the check at the beginning of vfio_user_get_region_info(), however that only happens _after_ the g_realloc, and if that value is excessively large it can cause g_realloc to fail, so check it here. Signed-off-by: Thanos Makatos Fixes: 667866d66620 ("vfio-user: implement VFIO_USER_DEVICE_GET_REGION_INFO") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-5-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 3d12e8b2a2..008e5cf687 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -170,6 +170,16 @@ static int vfio_user_get_region_info(VFIOUserProxy *proxy, return -EINVAL; } + /* + * The server can respond with a larger argsz in the reply to request a + * larger buffer on the next iteration via vfio_device_get_region_info(). + * Reject values that would trigger an oversized realloc. + */ + if (msgp->argsz > proxy->max_xfer_size) { + error_printf("vfio_user_get_region_info reply argsz too large\n"); + return -E2BIG; + } + memcpy(info, &msgp->argsz, info->argsz); /* From 20b3a363fa8c9a83715d7241ec21e2d516359a1b Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:26:56 +0000 Subject: [PATCH 102/860] vfio-user: vfio_user_device_io_get_region_info: fix capability check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing check for PCI capabilities misses the case where info->cap_offset == info->argsz, which results in accessing unallocated memory. Fix the comparison. Fixes: 667866d66620 ("vfio-user: implement VFIO_USER_DEVICE_GET_REGION_INFO") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3865 Signed-off-by: Thanos Makatos Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-6-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 008e5cf687..3d0dbe8406 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -214,7 +214,8 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, /* cap_offset in valid area */ if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) && - (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) { + (info->cap_offset < sizeof(*info) + || info->cap_offset + sizeof(struct vfio_info_cap_header) > info->argsz)) { return -EINVAL; } From 5f0cbed64c0fe153b58331c223f0afbf84f299c7 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:26:58 +0000 Subject: [PATCH 103/860] vfio-user: vfio_user_device_io_device_feature: prevent buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't in practise a problem since feature->argsz is not externally provided, it's a good hardening step nonetheless. Fixes: e2358af5838d ("vfio-user: support VFIO_USER_DEVICE_FEATURE") Signed-off-by: Thanos Makatos Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-7-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 3d0dbe8406..353fa5ee21 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -79,9 +79,14 @@ vfio_user_device_io_device_feature(VFIODevice *vbasedev, struct vfio_device_feature *feature) { g_autofree VFIOUserDeviceFeature *msgp = NULL; - int size = sizeof(VFIOUserHdr) + feature->argsz; VFIOUserProxy *proxy = vbasedev->proxy; Error *local_err = NULL; + int size; + + if (__builtin_add_overflow(feature->argsz, sizeof(VFIOUserHdr), &size)) { + error_printf("vfio_user_device_io_device_feature argsz too large\n"); + return -E2BIG; + } msgp = g_malloc0(size); From 761ac964a8a42f3d8b92dca150754c0cc8cbc560 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:26:59 +0000 Subject: [PATCH 104/860] vfio-user: vfio_user_device_io_device_feature: prevent excessive malloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't in practise a problem since feature->argsz is not externally provided, it's a good hardening step nonetheless. Fixes: e2358af5838d ("vfio-user: support VFIO_USER_DEVICE_FEATURE") Signed-off-by: Thanos Makatos Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-8-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 353fa5ee21..8feaa56875 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -87,6 +87,10 @@ vfio_user_device_io_device_feature(VFIODevice *vbasedev, error_printf("vfio_user_device_io_device_feature argsz too large\n"); return -E2BIG; } + if (size > proxy->max_xfer_size) { + error_printf("vfio_user_device_io_device_feature argsz too large\n"); + return -E2BIG; + } msgp = g_malloc0(size); From bf10c11af515e7de6343a429f9d6b505c504d531 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:27:00 +0000 Subject: [PATCH 105/860] vfio-user: vfio_user_device_io_set_irqs: prevent buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't in practise a problem since irq->argsz is not externally provided, it's a good hardening step nonetheless. Fixes: ca1add1696 ("vfio-user: implement VFIO_USER_DEVICE_GET/SET_IRQ*") Signed-off-by: Thanos Makatos Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-9-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 8feaa56875..daac0e21b6 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -293,7 +293,10 @@ static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev, * Handle simple case */ if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) { - size = sizeof(VFIOUserHdr) + irq->argsz; + if (__builtin_add_overflow(irq->argsz, sizeof(VFIOUserHdr), &size)) { + error_printf("vfio_user_set_irqs argsz too large\n"); + return -E2BIG; + } msgp = g_malloc0(size); vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0); From aef1e7c5ec80ee7a4484e4903c9e4c47d80535de Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Tue, 21 Jul 2026 12:27:02 +0000 Subject: [PATCH 106/860] vfio-user: vfio_user_device_io_set_irqs: prevent excessive malloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This isn't in practise a problem since irq->argsz is not externally provided, it's a good hardening step nonetheless. Fixes: ca1add1696 ("vfio-user: implement VFIO_USER_DEVICE_GET/SET_IRQ*") Signed-off-by: Thanos Makatos Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260721122643.30985-10-thanos.makatos@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index daac0e21b6..1a01e748cd 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -297,6 +297,11 @@ static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev, error_printf("vfio_user_set_irqs argsz too large\n"); return -E2BIG; } + if (size > proxy->max_xfer_size) { + error_printf("vfio_user_device_io_set_irqs argsz too large\n"); + return -E2BIG; + } + msgp = g_malloc0(size); vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0); From 21069e6b35bcff7da2e8605b5100d227ddbeb7c1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 22 Jun 2026 16:39:30 +0200 Subject: [PATCH 107/860] target/i386: tcg: remove FIXME Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 83c889cc3b..e4ef5a8758 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3424,7 +3424,9 @@ static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, /* * RCx operations are invariant modulo 8*operand_size+1. For 8 and 16-bit operands, * this is less than 0x1f (the mask applied by gen_shift_count) so reduce further. - * FIXME: are flags updated if the count is nonzero, but a multiple of (8 << op) + 1? + * A count that is a nonzero multiple of (8 << op) + 1 reduces to 0 and leaves + * CF and OF unmodified (confirmed against hardware that rcl $9,%al behaves + * exactly like rcl $0,%al). */ static MemOp gen_rotc_count(DisasContext *s, X86DecodedInsn *decode, bool *can_be_zero, TCGv *count, int unit) From 0e48aaae735f03592a619c69f476674fd2055823 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Mon, 13 Jul 2026 19:12:36 -0700 Subject: [PATCH 108/860] meson: propagate qemu_ldflags to Rust link step Sanitizer flags are added to qemu_ldflags and applied globally for all_languages, but all_languages never includes 'rust'. Fixes link errors like the ones below: -Wl,-rpath,$ORIGIN/../qemu-macros:/lib/rustlib/x86_64-unknown-linux-gnu/lib" = note: some arguments are omitted. use `--verbose` to show all linker arguments = note: rust-lld: error: undefined symbol: __ubsan_handle_type_mismatch_v1 >>> referenced by event-loop-base.c:104 (../qemu_before_fix/event-loop-base.c:104) >>> libevent-loop-base.a.p/event-loop-base.c.o:(event_loop_base_class_init) >>> referenced by event-loop-base.c:105 (../qemu_before_fix/event-loop-base.c:105) >>> libevent-loop-base.a.p/event-loop-base.c.o:(event_loop_base_class_init) >>> referenced by event-loop-base.c:58 (../qemu_before_fix/event-loop-base.c:58) >>> libevent-loop-base.a.p/event-loop-base.c.o:(event_loop_base_set_param) >>> referenced 11240 more times Signed-off-by: Brian Cain Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/r/20260714021236.2361604-1-brian.cain@oss.qualcomm.com Signed-off-by: Paolo Bonzini --- meson.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/meson.build b/meson.build index 164328ded8..5f42bbf40c 100644 --- a/meson.build +++ b/meson.build @@ -767,6 +767,9 @@ endif add_project_arguments(cc.get_supported_arguments(qemu_common_flags + qemu_cflags + warn_flags), native: false, language: 'c') add_global_link_arguments(qemu_ldflags, native: false, language: all_languages) +if have_rust + add_global_link_arguments(qemu_ldflags, native: false, language: 'rust') +endif if 'cpp' in all_languages add_project_arguments(cxx.get_supported_arguments(qemu_common_flags + qemu_cxxflags), From 51aa3f3e05772c38cb6f6d8e79eca0f6afaaace0 Mon Sep 17 00:00:00 2001 From: Andrey Polivoda Date: Mon, 8 Jun 2026 19:18:15 +1000 Subject: [PATCH 109/860] target/i386: helper_sysret(): Check that RCX contains a canonical address when emulating an Intel CPU Intel and AMD CPUs implement SYSRETQ instruction differently. One of these differences is whether a canonicality check of the address that will be loaded to RIP is performed: Intel CPUs do this check, AMD CPUs don't. Currently, QEMU does not perform this check when emulating Intel CPUs. This patch corrects this by implementing the canonlicality check on a new RIP value from RCX and performing it only when emulating Intel CPUs. Flags and segment registers' caches are updated only after checking the new RIP value to ensure that CPU state is not modified in case the #GP(0) exception is raised due to the check failure. Cc: qemu-devel@nongnu.org Cc: Paolo Bonzini Cc: Richard Henderson Fixes: 14ce26e75513 ("x86_64 target support") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3223 Signed-off-by: Andrey Polivoda Link: https://lore.kernel.org/r/20260608091815.31303-1-apolivodaa433@gmail.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/seg_helper.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 58aac72011..d5c174b7fd 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -1096,17 +1096,22 @@ void helper_sysret(CPUX86State *env, int dflag) selector = (env->star >> 48) & 0xffff; #ifdef TARGET_X86_64 if (env->hflags & HF_LMA_MASK) { - cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK - | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | - NT_MASK); if (dflag == 2) { + uint64_t new_rip = env->regs[R_ECX]; + if (IS_INTEL_CPU(env)) { + int shift = (get_pg_mode(env) & PG_MODE_LA57) ? 56 : 47; + int64_t sext = (int64_t)new_rip >> shift; + if (sext != 0 && sext != -1) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + } cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, 0, 0xffffffff, DESC_G_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK); - env->eip = env->regs[R_ECX]; + env->eip = new_rip; } else { cpu_x86_load_seg_cache(env, R_CS, selector | 3, 0, 0xffffffff, @@ -1120,6 +1125,10 @@ void helper_sysret(CPUX86State *env, int dflag) DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | (3 << DESC_DPL_SHIFT) | DESC_W_MASK | DESC_A_MASK); + + cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK + | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | + NT_MASK); } else #endif { From c3662589c4986fb7fcdda5ba6c71558e73ba759a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 20 Jul 2026 22:27:17 +0000 Subject: [PATCH 110/860] docs: fix sphinx build failure We recently started to have those failures appearing when building documentation. It's hard to identify if it comes from a python, sphinx or sphinx extension, but it blocks us. Exception occurred: File "/usr/lib/python3.13/multiprocessing/connection.py", line 399, in _recv raise EOFError EOFError The full traceback has been saved in /tmp/sphinx-*.log, if you want to report the issue to the developers. This seems to be the generic error message for "something went wrong in sphinx multiprocess": - https://github.com/sphinx-doc/sphinx/issues/11449 - https://github.com/sphinx-doc/sphinx/issues/14458 - https://github.com/sphinx-doc/sphinx/issues/8973 Solve the issue by simply going back to sequential builds. We didn't notice a huge speedup anyway. Reviewed-by: Stefan Hajnoczi Link: https://lore.kernel.org/qemu-devel/20260720222717.33078-1-pierrick.bouvier@oss.qualcomm.com Signed-off-by: Pierrick Bouvier --- docs/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/meson.build b/docs/meson.build index a8d893681e..4d8cc545fc 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -4,7 +4,7 @@ sphinx_build = find_program(fs.parent(python.full_path()) / 'sphinx-build', # Check if tools are available to build documentation. build_docs = false if sphinx_build.found() - SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build, '-q', '-j', 'auto'] + SPHINX_ARGS = ['env', 'CONFDIR=' + qemu_confdir, sphinx_build, '-q'] # If we're making warnings fatal, apply this to Sphinx runs as well if get_option('werror') SPHINX_ARGS += [ '-W', '-Dkerneldoc_werror=1' ] From cbd42e2b75b23953a9bdb073c8531518b1bd6163 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 21 Jul 2026 13:18:25 -0400 Subject: [PATCH 111/860] Update version for v11.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 a4efc3242f..f9e860890c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.0.90 +11.0.91 From d149a11c6c825e182cfb5db7d1d1492a049e82ab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 21 Jul 2026 17:22:11 +0200 Subject: [PATCH 112/860] scsi-disk: protect against guest sending truncated data for MODE SELECT commands scsi-disk has a MODE SELECT path where a truncated mode page can be allowed by a compatibility quirk, but the parser continues to use the page's declared length rather than the number of bytes actually remaining in the request buffer. This means that scsi_disk_check_mode_select() and scsi_disk_apply_mode_select() can read beyond the valid part of inbuf[], potentially up to the emulated age's length. Clamping page_len (the size of the page) to len (whatever the guest provided) ensures that scsi_disk_check_mode_select() and scsi_disk_apply_mode_select() do not access anything beyond bounds; however, this requires care to accept and handle truncated input in those two functions. In particular, until scsi_disk_check_mode_select()'s first call to mode_sense_page() the number of bytes to be cleared in mode_current[] is unknown, so zero it completely. And for everything else, be conservative and use len when providing inputs to other functions; but at the same time, ensure all accesses to inbuf[] are bound by expected_len. Note that pages longer than the emulated one are still rejected. Fixes: 389e18eb9aa4 ("scsi-disk: add SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED quirk for Macintosh") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4051 Tested-by: Mark Cave-Ayland Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 5ba5b46c4f..85d0bd0b81 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1524,7 +1524,7 @@ static void scsi_disk_emulate_read_data(SCSIRequest *req) static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, uint8_t *inbuf, int inlen) { - uint8_t mode_current[SCSI_MAX_MODE_LEN]; + uint8_t mode_current[SCSI_MAX_MODE_LEN] = { 0 }; uint8_t mode_changeable[SCSI_MAX_MODE_LEN]; uint8_t *p; int len, expected_len, changeable_len, i; @@ -1543,21 +1543,21 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, } p = mode_current; - memset(mode_current, 0, inlen + 2); len = mode_sense_page(s, page, &p, 0); - if (len < 0 || len != expected_len) { + /* The guest may send a truncated page, but not a longer one. */ + if (len < 0 || expected_len > len) { return -1; } p = mode_changeable; - memset(mode_changeable, 0, inlen + 2); + memset(mode_changeable, 0, len); changeable_len = mode_sense_page(s, page, &p, 1); assert(changeable_len == len); /* Check that unchangeable bits are the same as what MODE SENSE * would return. */ - for (i = 2; i < len; i++) { + for (i = 2; i < expected_len; i++) { if (((mode_current[i] ^ inbuf[i - 2]) & ~mode_changeable[i]) != 0) { return -1; } @@ -1565,11 +1565,15 @@ static int scsi_disk_check_mode_select(SCSIDiskState *s, int page, return 0; } -static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, uint8_t *p) +/* Note p may be truncated, so check any bytes you access against len. */ +static void scsi_disk_apply_mode_select(SCSIDiskState *s, int page, + uint8_t *p, int len) { switch (page) { case MODE_PAGE_CACHING: - blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + if (len > 0) { + blk_set_enable_write_cache(s->qdev.conf.blk, (p[0] & 4) != 0); + } break; default: @@ -1612,6 +1616,7 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param_len; } trace_scsi_disk_mode_select_page_truncated(page, page_len, len); + page_len = len; } if (!change) { @@ -1619,7 +1624,7 @@ static int mode_select_pages(SCSIDiskReq *r, uint8_t *p, int len, bool change) goto invalid_param; } } else { - scsi_disk_apply_mode_select(s, page, p); + scsi_disk_apply_mode_select(s, page, p, page_len); } p += page_len; From e2da3d92744d12b0c2e554ed533a4c9011dbd975 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 21 Jul 2026 17:56:42 +0200 Subject: [PATCH 113/860] scsi-disk: fix off by one in assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When documenting the invariant that mode pages need to fit the smallest output buffer of all callers (which is SCSI_MAX_MODE_LEN), the expression used by the assertion was incorrect. Even though SCSI_MAX_MODE_LEN is indeed 256, using "length < 256" had two issues: 1) it used the wrong operator, since "length < ..." is more related to having room for extra data; 2) it missed the extra two bytes for page number and length. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- hw/scsi/scsi-disk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 85d0bd0b81..1b0cce128c 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1321,7 +1321,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, return -1; } - assert(length < 256); + assert(length + 2 <= SCSI_MAX_MODE_LEN); (*p_outbuf)[0] = page; (*p_outbuf)[1] = length; *p_outbuf += length + 2; From 702216619e2a1afd5039520114f4d245d7011f09 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 Jul 2026 16:32:37 +0200 Subject: [PATCH 114/860] hw/uefi: add sanity check Verify the passed buffer has the minimal required length before reading the size field + verifying the total length. Fixes: CVE-2026-58581 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3614 Acked-by: Luigi Leonardi Signed-off-by: Gerd Hoffmann Message-ID: <20260720143244.821889-2-kraxel@redhat.com> --- hw/uefi/var-service-policy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c index 58da4adbeb..989bf87ddb 100644 --- a/hw/uefi/var-service-policy.c +++ b/hw/uefi/var-service-policy.c @@ -276,6 +276,9 @@ static uint32_t uefi_vars_mm_check_policy_register(uefi_vars_state *uv, uefi_var_policy *pol; uint64_t length; + if (mhdr->length < sizeof(*mchk) + sizeof(*pe)) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); + } if (uadd64_overflow(sizeof(*mchk), pe->size, &length)) { return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); } From ff5a9eb13c862ed274ea0d0682a6573411e31543 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 Jul 2026 16:32:38 +0200 Subject: [PATCH 115/860] hw/uefi: remove debug function This was never meant to be present in production builds. It's a code path not hit on a normal boot (OVMF wouldn't try variable updates which are not allowed), so this went unnoticed. Remove the function. If needed for debugging the git log is your friend. Fixes: CVE-2026-58582 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3615 Acked-by: Luigi Leonardi Signed-off-by: Gerd Hoffmann Message-ID: <20260720143244.821889-3-kraxel@redhat.com> --- hw/uefi/var-service-policy.c | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c index 989bf87ddb..0880bfe074 100644 --- a/hw/uefi/var-service-policy.c +++ b/hw/uefi/var-service-policy.c @@ -37,37 +37,6 @@ const VMStateDescription vmstate_uefi_var_policy = { }, }; -static void print_policy_entry(variable_policy_entry *pe) -{ - uint16_t *name = (void *)pe + pe->offset_to_name; - - fprintf(stderr, "%s:\n", __func__); - - fprintf(stderr, " name ´"); - while (*name) { - fprintf(stderr, "%c", *name); - name++; - } - fprintf(stderr, "', version=%d.%d, size=%d\n", - pe->version >> 16, pe->version & 0xffff, pe->size); - - if (pe->min_size) { - fprintf(stderr, " size min=%d\n", pe->min_size); - } - if (pe->max_size != UINT32_MAX) { - fprintf(stderr, " size max=%u\n", pe->max_size); - } - if (pe->attributes_must_have) { - fprintf(stderr, " attr must=0x%x\n", pe->attributes_must_have); - } - if (pe->attributes_cant_have) { - fprintf(stderr, " attr cant=0x%x\n", pe->attributes_cant_have); - } - if (pe->lock_policy_type) { - fprintf(stderr, " lock policy type %d\n", pe->lock_policy_type); - } -} - static gboolean wildcard_str_equal(uefi_var_policy *pol, uefi_variable *var) { @@ -173,7 +142,6 @@ efi_status uefi_vars_policy_check(uefi_vars_state *uv, pe = pol->entry; uefi_trace_variable(__func__, var->guid, var->name, var->name_size); - print_policy_entry(pe); if ((var->attributes & pe->attributes_must_have) != pe->attributes_must_have) { trace_uefi_vars_policy_deny("must-have-attr"); From 647ba95eda5a21518f84c6593ed97e0447f38a90 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 Jul 2026 16:32:39 +0200 Subject: [PATCH 116/860] hw/uefi: check lower limit for signature list size Specifically disallow zero which can lead to an endless loop. Fixes: CVE-2026-61406 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3899 Acked-by: Luigi Leonardi Signed-off-by: Gerd Hoffmann Message-ID: <20260720143244.821889-4-kraxel@redhat.com> --- hw/uefi/var-service-siglist.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/uefi/var-service-siglist.c b/hw/uefi/var-service-siglist.c index 8948f1b784..9e03a4dc0c 100644 --- a/hw/uefi/var-service-siglist.c +++ b/hw/uefi/var-service-siglist.c @@ -94,6 +94,9 @@ void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, break; } efilist = data; + if (efilist->siglist_size < sizeof(*efilist)) { + break; + } if (size < efilist->siglist_size) { break; } From 8e0ddb4a6ebd1c3d2dfd067f82b6d92de5b23e30 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 Jul 2026 16:32:40 +0200 Subject: [PATCH 117/860] hw/uefi: account variable policy entries against storage size uefi-vars already tracks (and limits) the memory footprint of UEFI variables. Do that for variable policies too. Fixes: CVE-2026-61405 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3890 Acked-by: Luigi Leonardi Signed-off-by: Gerd Hoffmann Message-ID: <20260720143244.821889-5-kraxel@redhat.com> --- hw/uefi/var-service-policy.c | 5 +++++ hw/uefi/var-service-vars.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c index 0880bfe074..46c4ff2dac 100644 --- a/hw/uefi/var-service-policy.c +++ b/hw/uefi/var-service-policy.c @@ -283,7 +283,12 @@ static uint32_t uefi_vars_mm_check_policy_register(uefi_vars_state *uv, return uefi_vars_mm_policy_error(mhdr, mchk, EFI_ALREADY_STARTED); } + if (uv->used_storage + pe->size > uv->max_storage) { + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_OUT_OF_RESOURCES); + } + uefi_vars_add_policy(uv, pe); + uv->used_storage += pe->size; mchk->result = EFI_SUCCESS; return sizeof(*mchk); diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index 2c83130ebf..fd07782f1e 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -201,11 +201,15 @@ void uefi_vars_clear_all(uefi_vars_state *uv) void uefi_vars_update_storage(uefi_vars_state *uv) { uefi_variable *var; + uefi_var_policy *pol; uv->used_storage = 0; QTAILQ_FOREACH(var, &uv->variables, next) { uv->used_storage += variable_size(var); } + QTAILQ_FOREACH(pol, &uv->var_policies, next) { + uv->used_storage += pol->entry->size; + } } static gboolean check_access(uefi_vars_state *uv, uefi_variable *var) From acba2d78176d8235b81fd886b37642ae6c464982 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 Jul 2026 16:32:41 +0200 Subject: [PATCH 118/860] hw/uefi: add post_load checks Add sanity checks to uefi-vars state loaded from live migration data stream. Fail migration if invalid data or inconsistencies are found. Fixes: CVE-2026-61404 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3837 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3838 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3839 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3885 Acked-by: Luigi Leonardi Signed-off-by: Gerd Hoffmann Message-ID: <20260720143244.821889-6-kraxel@redhat.com> --- hw/uefi/var-service-core.c | 16 ++++++++++++++-- hw/uefi/var-service-json.c | 5 +++++ hw/uefi/var-service-policy.c | 28 +++++++++++++++++++++++----- hw/uefi/var-service-vars.c | 15 +++++++++++++++ 4 files changed, 57 insertions(+), 7 deletions(-) diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index 828d760073..65da71029a 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -5,6 +5,7 @@ */ #include "qemu/osdep.h" #include "qemu/crc32c.h" +#include "qemu/error-report.h" #include "system/dma.h" #include "migration/vmstate.h" @@ -28,9 +29,20 @@ static int uefi_vars_post_load(void *opaque, int version_id) { uefi_vars_state *uv = opaque; - uefi_vars_update_storage(uv); - uefi_vars_json_save(uv); + if (uv->buf_size > MAX_BUFFER_SIZE) { + error_report("invalid buffer size"); + return -1; + } uv->buffer = g_malloc(uv->buf_size); + + uefi_vars_update_storage(uv); + if (uv->used_storage > uv->max_storage) { + error_report("out of variable memory (%" PRId64 " > %" PRId64 ")", + uv->used_storage, uv->max_storage); + return -1; + } + + uefi_vars_json_save(uv); return 0; } diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c index 8621b86c5c..88757d58b6 100644 --- a/hw/uefi/var-service-json.c +++ b/hw/uefi/var-service-json.c @@ -18,6 +18,7 @@ #include "qobject/qobject.h" #include "qobject/qjson.h" +#include "qapi/error.h" #include "qapi/dealloc-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" @@ -249,6 +250,10 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) if (!(*errp)) { uefi_vars_from_qapi(uv, vs); uefi_vars_update_storage(uv); + if (uv->used_storage > uv->max_storage) { + error_setg(errp, "out of variable memory (%" PRId64 " > %" PRId64 ")", + uv->used_storage, uv->max_storage); + } } qapi_free_UefiVarStore(vs); diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c index 46c4ff2dac..d43cc9b250 100644 --- a/hw/uefi/var-service-policy.c +++ b/hw/uefi/var-service-policy.c @@ -7,6 +7,7 @@ * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "system/dma.h" #include "migration/vmstate.h" @@ -16,14 +17,18 @@ #include "trace.h" -static void calc_policy(uefi_var_policy *pol); +static int check_calc_policy(uefi_var_policy *pol); static int uefi_var_policy_post_load(void *opaque, int version_id) { uefi_var_policy *pol = opaque; + int rc; - calc_policy(pol); - return 0; + rc = check_calc_policy(pol); + if (rc != 0) { + error_report("invalid uefi variable policy"); + } + return rc; } const VMStateDescription vmstate_uefi_var_policy = { @@ -80,32 +85,45 @@ static uefi_var_policy *wildcard_find_policy(uefi_vars_state *uv, return NULL; } -static void calc_policy(uefi_var_policy *pol) +static int check_calc_policy(uefi_var_policy *pol) { variable_policy_entry *pe = pol->entry; unsigned int i; + if (pol->entry_size != pe->size || + pe->offset_to_name >= pe->size) { + return -1; + } + pol->name = (void *)pol->entry + pe->offset_to_name; pol->name_size = pe->size - pe->offset_to_name; + if (!uefi_str_is_valid(pol->name, pol->name_size, false)) { + return -1; + } + for (i = 0; i < pol->name_size / 2; i++) { if (pol->name[i] == '#') { pol->hashmarks++; } } + + return 0; } uefi_var_policy *uefi_vars_add_policy(uefi_vars_state *uv, variable_policy_entry *pe) { uefi_var_policy *pol, *p; + int rc; pol = g_new0(uefi_var_policy, 1); pol->entry = g_malloc(pe->size); memcpy(pol->entry, pe, pe->size); pol->entry_size = pe->size; - calc_policy(pol); + rc = check_calc_policy(pol); + g_assert(rc == 0); /* keep list sorted by priority, add to tail of priority group */ QTAILQ_FOREACH(p, &uv->var_policies, next) { diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index fd07782f1e..e0ea3d532e 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -47,6 +47,20 @@ static int uefi_vars_pre_load(void *opaque) return 0; } +static int uefi_vars_post_load(void *opaque, int version_id) +{ + uefi_variable *var = opaque; + + if (!uefi_str_is_valid(var->name, var->name_size, true) || + var->attributes & ~EFI_VARIABLE_ATTRIBUTE_SUPPORTED || + (var->digest_size != 0 && + var->digest_size != 32 /* AUTHVAR_DIGEST_SIZE */)) { + error_report("invalid uefi variable"); + return -1; + } + return 0; +} + static bool uefi_vars_digest_is_needed(void *opaque) { uefi_variable *var = opaque; @@ -72,6 +86,7 @@ const VMStateDescription vmstate_uefi_variable_digest = { const VMStateDescription vmstate_uefi_variable = { .name = "uefi-variable", .pre_load = uefi_vars_pre_load, + .post_load = uefi_vars_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0), VMSTATE_UINT32(name_size, uefi_variable), From bd9b3c50f458ce24fee084916cf0eba58bd9a33b Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 20 Jul 2026 16:32:42 +0200 Subject: [PATCH 119/860] hw/uefi: make SetupMode read-only This is read-only variable which informs the OS about the secure boot state. Reject any attempts to write to it. Fixes: CVE-2026-16288 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4039 Acked-by: Luigi Leonardi Signed-off-by: Gerd Hoffmann Message-ID: <20260720143244.821889-7-kraxel@redhat.com> --- hw/uefi/var-service-auth.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/uefi/var-service-auth.c b/hw/uefi/var-service-auth.c index f3dc9c6ca6..899444af12 100644 --- a/hw/uefi/var-service-auth.c +++ b/hw/uefi/var-service-auth.c @@ -298,6 +298,13 @@ efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var) return EFI_WRITE_PROTECTED; } + /* reject SetupMode updates */ + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && + uefi_str_equal(var->name, var->name_size, + name_setup_mode, sizeof(name_setup_mode))) { + return EFI_WRITE_PROTECTED; + } + return EFI_SUCCESS; } From 1667cf6b7dc393c4887327144944db14c25d4b38 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Thu, 16 Jul 2026 09:29:26 +0300 Subject: [PATCH 120/860] spelling: happend Fixes: 44adb5fcc20d "target/riscv: Remove spike as default machine" Fixes: 48814111366b "migration: Always set DEVICE state" Fixes: 3345fb3b6d7f "migration/postcopy: Add latency distribution report for blocktime" Signed-off-by: Michael Tokarev --- docs/about/removed-features.rst | 2 +- migration/migration.c | 2 +- qapi/migration.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index f7a2aa326d..0735b4ea1d 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1241,7 +1241,7 @@ RISC-V default machine (removed in 11.1) '''''''''''''''''''''''''''''''''''''''' RISC-V used to define ``spike`` as the default machine if no machine option -was given via the command line. This happend because ``spike`` was the first +was given via the command line. This happened because ``spike`` was the first RISC-V machine implemented in QEMU and setting it as default was convenient at that time. Now we have 7 riscv64 and 6 riscv32 machines and having ``spike`` as a default is no longer justified. diff --git a/migration/migration.c b/migration/migration.c index 9736c14458..9df6da131c 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2806,7 +2806,7 @@ static bool migration_switchover_prepare(MigrationState *s) bql_lock(); /* * After BQL released and retaken, the state can be CANCELLING if it - * happend during sem_wait().. Only change the state if it's still + * happened during sem_wait().. Only change the state if it's still * pre-switchover. */ migrate_set_state(&s->state, MIGRATION_STATUS_PRE_SWITCHOVER, diff --git a/qapi/migration.json b/qapi/migration.json index 66bd900465..b1eaf7b054 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -270,8 +270,8 @@ # (Since 10.1) # # @postcopy-non-vcpu-latency: average remote page fault latency for -# all faults happend in non-vCPU threads (in ns). It has the same -# definition of @postcopy-latency but this only provides +# all faults happened in non-vCPU threads (in ns). It has the +# same definition of @postcopy-latency but this only provides # statistics to non-vCPU faults. This is only present when the # postcopy-blocktime migration capability is enabled. # (Since 10.1) From dacbde0ad76a45f73b7dd7f28953ab386371e932 Mon Sep 17 00:00:00 2001 From: Filip Hejsek Date: Thu, 18 Jun 2026 13:20:18 +0200 Subject: [PATCH 121/860] char-win-stdio: fix typo in spelling of 'stdio' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filip Hejsek Reviewed-by: Marc-André Lureau Reviewed-by: Stefan Weil Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- chardev/char-win-stdio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index bb9c195a8b..0deba3bffb 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -128,7 +128,7 @@ static void win_stdio_thread_wait_func(void *opaque) SetEvent(stdio->hInputDoneEvent); } -static void win_stiod_chr_set_echo(Chardev *chr, bool echo) +static void win_stdio_chr_set_echo(Chardev *chr, bool echo) { WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); DWORD dwMode = 0; @@ -205,7 +205,7 @@ static bool win_stdio_chr_open(Chardev *chr, SetConsoleMode(stdio->hStdIn, dwMode); - win_stiod_chr_set_echo(chr, false); + win_stdio_chr_set_echo(chr, false); qemu_chr_be_event(chr, CHR_EVENT_OPENED); return true; @@ -263,7 +263,7 @@ static void char_win_stdio_class_init(ObjectClass *oc, const void *data) cc->chr_open = win_stdio_chr_open; cc->chr_write = win_stdio_chr_write; - cc->chr_set_echo = win_stiod_chr_set_echo; + cc->chr_set_echo = win_stdio_chr_set_echo; } static const TypeInfo char_win_stdio_type_info = { From 68dd6989abe5212fc89ef0fcb6fedc460bfd1cb7 Mon Sep 17 00:00:00 2001 From: Vladimir Lobanov Date: Wed, 17 Jun 2026 12:44:56 +0300 Subject: [PATCH 122/860] block/curl: set User-Agent header Some HTTP servers and WAFs (e.g. Amazon CloudFront) reject requests without a User-Agent header with 403 Forbidden. This makes qemu-img info and other curl-based operations fail on such URLs without any obvious indication of the root cause. Set a "QEMU/" User-Agent string on all curl handles to ensure compatibility with these endpoints. Signed-off-by: Vladimir Lobanov Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- block/curl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/block/curl.c b/block/curl.c index 684c677ef7..1958eb80d1 100644 --- a/block/curl.c +++ b/block/curl.c @@ -482,6 +482,8 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) } } if (curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout) || + curl_easy_setopt(state->curl, CURLOPT_USERAGENT, + "QEMU/" QEMU_VERSION) || curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb) || curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) || From f604b807a55acd3f81725bb7dbd98450c96adf00 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 23 Jul 2026 14:44:32 +0200 Subject: [PATCH 123/860] hw/display/vmware_vga: Don't allow guest to trigger long running loop in host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code in the SVGA_CMD_DEFINE_ALPHA_CURSOR handler in vmsvga_fifo_run() basically does: x = vmsvga_fifo_read(s); y = vmsvga_fifo_read(s); args = x * y; goto badcmd; ... badcmd: len -= args; if (len < 0) { goto rewind; } while (args--) { vmsvga_fifo_read(s); } Thus by supplying huge values for x and y that overflow the result of the multiplication, the guest can trigger a long-running loop here that burns the host's CPU cycles. Add some sanity checks so that this cannot happen anymore. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3782 Reported-by: Feifan Qian Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4026 Reported-by: Tristan Madani Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4076 Reported-by: Sunday Jiang Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/display/vmware_vga.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index f6f9edfd1d..567806f0e4 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -737,6 +737,10 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) vmsvga_fifo_read(s); x = vmsvga_fifo_read(s); y = vmsvga_fifo_read(s); + if (x < 0 || x >= SVGA_MAX_WIDTH || + y < 0 || y >= SVGA_MAX_HEIGHT) { + goto rewind; + } args = x * y; goto badcmd; case SVGA_CMD_RECT_ROP_FILL: @@ -776,7 +780,7 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) if (len < 0) { goto rewind; } - while (args--) { + while (args-- > 0) { vmsvga_fifo_read(s); } printf("%s: Unknown command 0x%02x in SVGA command FIFO\n", From d915f9d607880e1b05293f098a8988be7a496abc Mon Sep 17 00:00:00 2001 From: Evgeny Kolmakov Date: Thu, 9 Jul 2026 23:27:22 +0300 Subject: [PATCH 124/860] hw/hyperv/vmbus: Use QEMU_LOCK_GUARD() Replace manual qemu_mutex_(un)lock() calls with QEMU_LOCK_GUARD() to remove 'goto out' code Signed-off-by: Evgeny Kolmakov Reviewed-by: Maciej S. Szmigiero Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/hyperv/vmbus.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index c5259d1a76..ab99f082d1 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -1525,14 +1525,12 @@ static VMBusChannel *find_channel(VMBus *vmbus, uint32_t id) static int enqueue_incoming_message(VMBus *vmbus, const struct hyperv_post_message_input *msg) { - int ret = 0; uint8_t idx, prev_size; - qemu_mutex_lock(&vmbus->rx_queue_lock); + QEMU_LOCK_GUARD(&vmbus->rx_queue_lock); if (vmbus->rx_queue_size == HV_MSG_QUEUE_LEN) { - ret = -ENOBUFS; - goto out; + return -ENOBUFS; } prev_size = vmbus->rx_queue_size; @@ -1544,9 +1542,7 @@ static int enqueue_incoming_message(VMBus *vmbus, if (!prev_size) { vmbus_resched(vmbus); } -out: - qemu_mutex_unlock(&vmbus->rx_queue_lock); - return ret; + return 0; } static uint16_t vmbus_recv_message(const struct hyperv_post_message_input *msg, @@ -2097,10 +2093,10 @@ static void process_message(VMBus *vmbus) void *msgdata; uint32_t msglen; - qemu_mutex_lock(&vmbus->rx_queue_lock); + QEMU_LOCK_GUARD(&vmbus->rx_queue_lock); if (!vmbus->rx_queue_size) { - goto unlock; + return; } hv_msg = &vmbus->rx_queue[vmbus->rx_queue_head]; @@ -2149,8 +2145,6 @@ out: vmbus->rx_queue_head %= HV_MSG_QUEUE_LEN; vmbus_resched(vmbus); -unlock: - qemu_mutex_unlock(&vmbus->rx_queue_lock); } static const struct { From a525551d86aed37fe0e7425049be523b777a1fd5 Mon Sep 17 00:00:00 2001 From: Ziyi Fu Date: Fri, 26 Jun 2026 10:05:53 +0200 Subject: [PATCH 125/860] hw/usb: record async control completion for parameter transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Record the completion side of parameter-based control transfers when they complete asynchronously. This lets pcap captures include descriptor response data for requests such as GET_DESCRIPTOR from usb-host devices. The synchronous path already recorded the completion, but the async SETUP_STATE_PARAM path was missing it. Signed-off-by: Ziyi Fu Reviewed-by: Marc-André Lureau Fixes: 0f6dba145a4b ("usb: add pcap support.") Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/usb/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/usb/core.c b/hw/usb/core.c index 9572a870cc..d71204c5c8 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -345,6 +345,7 @@ void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p) p->actual_length = 0; usb_packet_copy(p, s->data_buf, s->setup_len); } + usb_pcap_ctrl(p, false); break; default: From 443e02410695b35a27fad18217424b6370358be7 Mon Sep 17 00:00:00 2001 From: Artem Nasonov Date: Wed, 22 Jul 2026 12:41:11 +0200 Subject: [PATCH 126/860] hw/ide: replace assert with proper error handling In ide_dma_cb(), the call to prepare_buf() might return a negative result and cause an assertion failure. This was found during fuzzing and can be triggered with some qtest commands. Replace the assert with proper error handling in case the result is negative, but keep the assert for failing to respect the limit upon success. If that happens, it is an implementation error. Found by Linux Verification Center (linuxtesting.org) with libFuzzer. Cc: qemu-stable@nongnu.org Fixes: ed78352a59 ("ide: Fix incorrect handling of some PRDTs in ide_dma_cb()") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2777 Signed-off-by: Artem Nasonov Link: https://lore.kernel.org/qemu-devel/20250116111600.2570490-1-anasonov@astralinux.ru [FE: improve commit message keep assert for failing to respect the limit] Signed-off-by: Fiona Ebner Reviewed-by: Thomas Huth Signed-off-by: Michael Tokarev --- hw/ide/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index f78b00220b..4c1ee19d8e 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -921,8 +921,12 @@ static void ide_dma_cb(void *opaque, int ret) s->io_buffer_index = 0; s->io_buffer_size = n * 512; prep_size = s->bus->dma->ops->prepare_buf(s->bus->dma, s->io_buffer_size); - /* prepare_buf() must succeed and respect the limit */ - assert(prep_size >= 0 && prep_size <= n * 512); + if (prep_size < 0) { + ide_dma_error(s); + return; + } + /* If prepare_buf() succeeds, it must respect the limit. */ + assert(prep_size <= n * 512); /* * Now prep_size stores the number of bytes in the sglist, and From 7858dbb36710edbb9697286a925e170effd9e879 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 3 Jul 2026 17:38:10 +0200 Subject: [PATCH 127/860] tests/9p: prevent potential error in do_local_xattr_limit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The local fs backend driver tests require that a dummy file is created on the host side before running the xattr limit tests, otherwise the test will fail because the file is expected to exist. The original call of g_file_set_contents() didn't check its return value, which might cause subsequent test checks to fail, making it harder to identify the root cause. Fix this by simply wrapping the call into a g_assert(). Fixes: 04a62cdfe873 ("tests/9p: add 3 xattr FID limit test cases (local fs driver)") CID: 1660926 Tested-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/E1wfg2F-001Sfn-Rr@kylie.crudebyte.com Signed-off-by: Christian Schoenebeck --- tests/qtest/virtio-9p-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index cfd3c02da4..692a0e8e3f 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -402,7 +402,7 @@ static void do_local_xattr_limit(QVirtio9P *v9p, int max_xattr) /* * this file must be created for the test to work with the 'local' fs driver */ - g_file_set_contents(test_file, "", 0, NULL); + g_assert(g_file_set_contents(test_file, "", 0, NULL)); /* the actual test code shared with the 'synth' fs driver tests */ do_xattr_limit(v9p, max_xattr, false); From d55b1234739c64fc6b7e0634613faf141bf91731 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 3 Jul 2026 17:26:08 +0200 Subject: [PATCH 128/860] tests/9p: reduce xattr size to 1k On host systems with ext4 and ea_inode capability not enabled, the max. size for xattr values is slightly below 4k, which caused the new xattr tests to fail on such host systems. Reduce the xattr size for our tests to 1k to prevent them to fail on such host systems. Reported-by: Pierrick Bouvier Fixes: 04a62cdfe8 ("tests/9p: add 3 xattr FID limit test cases (local fs driver)") Reviewed-by: Pierrick Bouvier Tested-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/E1wffqw-001Sc8-I9@kylie.crudebyte.com Signed-off-by: Christian Schoenebeck --- tests/qtest/virtio-9p-test.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index 692a0e8e3f..22302bd2f8 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -38,11 +38,17 @@ /* * xattr size to be used for xattr tests * - * 64k is the max. xattr size supported by the Linux kernel, However btrfs - * for instance supports only 16219 bytes. So let's be conservative and - * just use 8k for the xattr tests. + * 64k is the max. xattr size supported by the Linux kernel on high-level VFS + * layer. However filesystems impose their own limits: + * + * - btrfs: 16219 bytes + * + * - ext4: blocksize - 56 bytes (~4KB) if no ea_inode capability enabled, + * 64k if ea_inode enabled + * + * So let's be conservative and just use 1k for the xattr tests. */ -#define TEST_XATTR_SIZE (8 * 1024) +#define TEST_XATTR_SIZE (1 * 1024) static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) { From a0414545a212e27058fab7b057b018e75b8c4b13 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Wed, 15 Jul 2026 18:10:00 +0200 Subject: [PATCH 129/860] hw/9pfs: fix O_TRUNC bypass on read-only export MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guest 9p client opening a file with O_TRUNC on a read-only 9p file system using 9p2000.u protocol version, allowed to bypass 9p server's read-only check, eventually causing file(s) being truncated to empty file(s) on host's read-only export. Root cause is that 9p server's read-only check is using Linux open flags like O_WRONLY, O_RDWR, O_TRUNC, but checking them against the 9p Topen request's "mode" parameter, which has a different encoding (Otrunc = 0x10 vs. O_TRUNC = 0x200). Fix this by checking against the "flags" variable instead of the protocol's "mode" option. Because the "flags" variable is already converted to Linux encoding by omode_to_uflags() for 9p2000.u and by get_dotl_openflags() for 9p2000.L protocol version. Only 9p2000.u was affected by this bypass, 9p2000.L uses the Linux format on protocol level already. Fixes: 2c74c2cb4b ("hw/9pfs: Read-only support for 9p export") Fixes: CVE-2026-63318 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4000 Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/E1wk2Dq-0019kY-JK@kylie.crudebyte.com Signed-off-by: Christian Schoenebeck --- hw/9pfs/9p.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 3119f01117..474ac6cc40 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -2181,8 +2181,8 @@ static void coroutine_fn v9fs_open(void *opaque) flags = omode_to_uflags(mode); } if (is_ro_export(&s->ctx)) { - if (mode & O_WRONLY || mode & O_RDWR || - mode & O_APPEND || mode & O_TRUNC) { + if (flags & O_WRONLY || flags & O_RDWR || + flags & O_APPEND || flags & O_TRUNC) { err = -EROFS; goto out; } From 210701c82e304622818989028e75192013cb81d8 Mon Sep 17 00:00:00 2001 From: Jia Jia Date: Thu, 23 Jul 2026 14:43:24 +0200 Subject: [PATCH 130/860] hw/9pfs/virtio: drain in-flight PDUs before virtio-9p unrealize A guest can trigger a heap-use-after-free in the virtio transport unrealize path by submitting a Treaddir request and immediately ejecting the device via ACPI PCI hotplug. The unrealize path frees struct LocalData while a worker thread still holds a reference on it, causing a UAF in local_open_nofollow(). Fix this by draining all in-flight 9p PDUs by calling v9fs_reset() before final server cleanup. This ensures all coroutines completed, all FIDs are closed, and no worker thread still holds references on 9p server state when it is freed. Fixes: 6cecf09373 ("virtio-9p-device: add minimal unrealize handler") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3937 Signed-off-by: Jia Jia [ Christian Schoenebeck: add commit log message. ] Link: https://lore.kernel.org/qemu-devel/bc503aefffd20b1806941b3ef708afbd92f0aef2.1784809978.git.qemu_oss@crudebyte.com Signed-off-by: Christian Schoenebeck --- hw/9pfs/virtio-9p-device.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 50dc93091d..576a794ea4 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -243,6 +243,7 @@ static void virtio_9p_device_unrealize(DeviceState *dev) V9fsVirtioState *v = VIRTIO_9P(dev); V9fsState *s = &v->state; + v9fs_reset(s); /* cancel all in-flight PDUs to prevent UAF */ virtio_delete_queue(v->vq); virtio_cleanup(vdev); v9fs_device_unrealize_common(s); From 1de8aea061b9d5bc7aaed182e2eebe9554debd68 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Thu, 23 Jul 2026 14:43:24 +0200 Subject: [PATCH 131/860] hw/9pfs/xen: drain in-flight PDUs before xen-9p disconnect The xen-9p disconnect path has two issues: 1. It frees the Xen9pfsRing structures while in-flight PDUs may still reference them via pdu->tag to index rings[]. This causes a UAF in xen_9pfs_push_and_notify() when worker threads resume after completing filesystem operations. 2. It never calls v9fs_device_unrealize_common(), which means server state (struct LocalData, mountfd, FIDs) is never cleaned up on disconnect, causing a resource leak on every guest-initiated disconnect. Fix both by draining in-flight PDUs via v9fs_reset() before tearing down rings, and calling v9fs_device_unrealize_common() to clean up server state. Additionally, explicit calls of xen_9pfs_disconnect() in the error paths of xen_9pfs_pdu_vmarshal() and xen_9pfs_pdu_vunmarshal() must be deferred (via aio_bh_schedule_oneshot()), because xen_9pfs_pdu_v(un)marshal() are running within a coroutine context which makes them unsafe [1] for calling v9fs_reset() directly, as the latter e.g. has a loop like: while (!QLIST_EMPTY(&s->active_list)) { aio_poll(qemu_get_aio_context(), true); } which would a) never terminate (as the coroutine is on the active_list) and b) aio_poll() is marked as no_coroutine_fn. [1] https://lore.kernel.org/qemu-devel/3351181.5fSG56mABF@weasel/ And finally, add an idempotent guard to xen_9pfs_disconnect() for the v9fs_reset(s) and v9fs_device_unrealize_common(s) calls specifically [2], just to be sure. [2] https://lore.kernel.org/qemu-devel/alpine.DEB.2.22.394.2607221815520.5295@ubuntu-linux-20-04-desktop/ Fixes: b37eeb0201 ("xen/9pfs: introduce Xen 9pfs backend") Reviewed-by: Stefano Stabellini Link: https://lore.kernel.org/qemu-devel/82bc736158e827e05d4b55da27c39d42e2062e96.1784809978.git.qemu_oss@crudebyte.com Signed-off-by: Christian Schoenebeck --- hw/9pfs/9p.c | 1 + hw/9pfs/xen-9p-backend.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 474ac6cc40..1c61e6b3e7 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -4530,6 +4530,7 @@ void v9fs_device_unrealize_common(V9fsState *s) qp_table_destroy(&s->qpp_table); qp_table_destroy(&s->qpf_table); g_free(s->ctx.fs_root); + s->transport = NULL; } typedef struct VirtfsCoResetData { diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 24c90d97ec..d44fa8d61e 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -68,6 +68,11 @@ typedef struct Xen9pfsDev { static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); +static void xen_9pfs_disconnect_bh(void *opaque) +{ + xen_9pfs_disconnect(opaque); +} + static void xen_9pfs_in_sg(Xen9pfsRing *ring, struct iovec *in_sg, int *num, @@ -150,7 +155,8 @@ static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, "Failed to encode VirtFS reply type %d\n", pdu->id + 1); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } @@ -173,7 +179,8 @@ static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, xen_pv_printf(&xen_9pfs->xendev, 0, "Failed to decode VirtFS request type %d\n", pdu->id); xen_be_set_state(&xen_9pfs->xendev, XenbusStateClosing); - xen_9pfs_disconnect(&xen_9pfs->xendev); + aio_bh_schedule_oneshot(qemu_get_aio_context(), + xen_9pfs_disconnect_bh, &xen_9pfs->xendev); } return ret; } @@ -368,10 +375,16 @@ static void xen_9pfs_evtchn_event(void *opaque) static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) { Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); + V9fsState *s = &xen_9pdev->state; int i; trace_xen_9pfs_disconnect(xendev->name); + if (s->transport) { + v9fs_reset(s); /* cancel all in-flight PDUs to prevent UAF */ + v9fs_device_unrealize_common(s); + } + for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].evtchndev != NULL) { qemu_set_fd_handler(qemu_xen_evtchn_fd(xen_9pdev->rings[i].evtchndev), From 8268d216a004d884a07db20e973158d81760a07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 22 Jul 2026 14:35:45 +0200 Subject: [PATCH 132/860] linux-user: Guard local FUTEX_CMD_MASK definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building linux-user on a host with Linux 7.2 kernel headers fails with a macro redefinition error for FUTEX_CMD_MASK. The kernel commit 3ca9595d9fb6 ("futex: Add support for unlocking robust futexes") expanded the mask to include the new FUTEX_ROBUST_UNLOCK and FUTEX_ROBUST_LIST32 flags, which conflicts with QEMU's local definition. Add a #ifndef guard so the host definition takes precedence when available. The local fallback is kept for older kernel headers (pre-2.6.29) that lack FUTEX_CMD_MASK or define a mask without FUTEX_CLOCK_REALTIME. Signed-off-by: Cédric Le Goater Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Helge Deller --- linux-user/syscall_defs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 5799769f83..e033c7db34 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2593,7 +2593,9 @@ struct target_drm_i915_getparam { #define FUTEX_PRIVATE_FLAG 128 #define FUTEX_CLOCK_REALTIME 256 +#ifndef FUTEX_CMD_MASK #define FUTEX_CMD_MASK ~(FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME) +#endif #if defined(TARGET_X86_64) #define TARGET_EPOLL_PACKED QEMU_PACKED From d71fa99ca8769947ec4f59790fde4b0bf4a4e295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 20 Jul 2026 18:23:21 +0200 Subject: [PATCH 133/860] tests/functional/aspeed: Fix remaining hostname-specific login prompts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b04746bd0d7c unified boot completion detection on the generic 'login:' prompt but missed several SDK test files that still matched on hostname-prefixed patterns. Apply the same fix. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4059 Fixes: b04746bd0d7c ("tests/functional/aspeed: unify boot completion detection on 'login:' prompt") Reported-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Peter Maydell Link: https://lore.kernel.org/qemu-devel/20260720162321.3497633-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700a1.py | 2 +- tests/functional/aarch64/test_aspeed_ast2700a2.py | 2 +- tests/functional/aarch64/test_aspeed_ast2700fc.py | 2 +- tests/functional/arm/test_aspeed_ast2500_sdk.py | 2 +- tests/functional/arm/test_aspeed_ast2500_sdk_515.py | 2 +- tests/functional/arm/test_aspeed_ast2600_sdk.py | 2 +- tests/functional/arm/test_aspeed_ast2600_sdk_515.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700a1.py b/tests/functional/aarch64/test_aspeed_ast2700a1.py index b0c08854da..ddddd2d47a 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a1.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a1.py @@ -81,7 +81,7 @@ class AST2x00MachineSDK(QemuSystemTest): def verify_openbmc_boot_and_login(self, name, enable_pcie=True): self.verify_openbmc_boot_start(enable_pcie) - wait_for_console_pattern(self, f'{name} login:') + wait_for_console_pattern(self, 'login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') diff --git a/tests/functional/aarch64/test_aspeed_ast2700a2.py b/tests/functional/aarch64/test_aspeed_ast2700a2.py index ed414999f4..0fe04a6eca 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a2.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a2.py @@ -81,7 +81,7 @@ class AST2x00MachineSDK(QemuSystemTest): def verify_openbmc_boot_and_login(self, name, enable_pcie=True): self.verify_openbmc_boot_start(enable_pcie) - wait_for_console_pattern(self, f'{name} login:') + wait_for_console_pattern(self, 'login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index df889134ed..86270e6111 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -51,7 +51,7 @@ class AST2x00MachineSDK(QemuSystemTest): self.enable_ast2700_pcie2() wait_for_console_pattern(self, 'Starting kernel ...') - wait_for_console_pattern(self, f'{name} login:') + wait_for_console_pattern(self, 'login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') diff --git a/tests/functional/arm/test_aspeed_ast2500_sdk.py b/tests/functional/arm/test_aspeed_ast2500_sdk.py index 5ab36b99ab..6ab498b3ff 100755 --- a/tests/functional/arm/test_aspeed_ast2500_sdk.py +++ b/tests/functional/arm/test_aspeed_ast2500_sdk.py @@ -22,7 +22,7 @@ class AST2500Machine(AspeedTest): self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) - self.wait_for_console_pattern('ast2500-default login:') + self.wait_for_console_pattern('login:') if __name__ == '__main__': diff --git a/tests/functional/arm/test_aspeed_ast2500_sdk_515.py b/tests/functional/arm/test_aspeed_ast2500_sdk_515.py index 2b257986f6..8d39dc65da 100755 --- a/tests/functional/arm/test_aspeed_ast2500_sdk_515.py +++ b/tests/functional/arm/test_aspeed_ast2500_sdk_515.py @@ -22,7 +22,7 @@ class AST2500Machine(AspeedTest): self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default-515", "image-bmc")) - self.wait_for_console_pattern('ast2500-default-515 login:') + self.wait_for_console_pattern('login:') if __name__ == '__main__': diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk.py b/tests/functional/arm/test_aspeed_ast2600_sdk.py index 01548dd135..cabbe230c4 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk.py @@ -63,7 +63,7 @@ class AST2600Machine(AspeedTest): self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2600-default-image", "image-bmc")) - self.wait_for_console_pattern('ast2600-default login:') + self.wait_for_console_pattern('login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk_515.py b/tests/functional/arm/test_aspeed_ast2600_sdk_515.py index ec043e7d61..a8e7faff48 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk_515.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk_515.py @@ -22,7 +22,7 @@ class AST2600Machine(AspeedTest): self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2600-default-515-image", "image-bmc")) - self.wait_for_console_pattern('ast2600-default-515 login:') + self.wait_for_console_pattern('login:') if __name__ == '__main__': From b71a179148ff49ed236cffb98af8891aeed94159 Mon Sep 17 00:00:00 2001 From: Feifan Qian Date: Fri, 12 Jun 2026 15:37:53 +0000 Subject: [PATCH 134/860] hw/cxl: Validate Set Feature payload bounds cmd_features_set_feature() derives bytes_to_copy from the mailbox input length and uses hdr->offset as the destination offset into per-feature write attribute buffers. The patrol scrub and ECS paths already reject writes where hdr->offset plus bytes_to_copy exceeds the destination structure. Add the same check to the soft PPR, hard PPR and memory sparing feature paths before copying into their write attribute buffers. Without the check, a malformed Set Feature request can write past the selected write attribute object and corrupt adjacent CXL type 3 device state. Fixes: 5e5a86bab830 ("hw/cxl: Add support for Maintenance command and Post Package Repair (PPR)") Fixes: da5cafdc4ddd ("hw/cxl: Add emulation for memory sparing control feature") Signed-off-by: Feifan Qian Reviewed-by: Thomas Huth Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3458 Reported-by: Jia Jia Signed-off-by: Thomas Huth --- hw/cxl/cxl-mailbox-utils.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 20e0b7e476..ec18338b42 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1813,6 +1813,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->soft_ppr_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->soft_ppr_wr_attrs + hdr->offset, sppr_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1832,6 +1836,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->hard_ppr_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->hard_ppr_wr_attrs + hdr->offset, hppr_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1851,6 +1859,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->cacheline_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->cacheline_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1869,6 +1881,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->row_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->row_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1887,6 +1903,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->bank_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->bank_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; @@ -1905,6 +1925,10 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd, return CXL_MBOX_UNSUPPORTED; } + if ((uint32_t)hdr->offset + bytes_to_copy > + sizeof(ct3d->rank_sparing_wr_attrs)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset, mem_sparing_write_attrs, bytes_to_copy); set_feat_info->data_size += bytes_to_copy; From b7520c9a59128d7e616876a511821c70edab2a90 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 20 Jul 2026 15:48:09 +0200 Subject: [PATCH 135/860] hw/usb/dev-uas: Fix guest-triggerable heap OOB access The stream ID is under control of the guest, and some spots in the code currently use it for indexing into the status3[] array without checking it for being in range first, so the code accesses the heap beyond the limit of the status3 array. Since our status delivery code depends on having a valid stream ID, we must not try to generate a fake sense code in this situation. Simply log a guest error and return early in usb_uas_command(). And to make sure that we really cannot access the status3[] array beyond its limit anymore, add some assert() statements in the affected functions, too. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3612 Reported-by: Reported-by: huntr bubble Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3986 Reported-by: Tristan Madani Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth Message-ID: <20260720134809.573757-1-thuth@redhat.com> --- hw/usb/dev-uas.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 8576dfec96..963c0433b3 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -362,6 +362,7 @@ static void usb_uas_send_status_bh(void *opaque) while ((st = QTAILQ_FIRST(&uas->results)) != NULL) { if (uas_using_streams(uas)) { + assert(st->stream <= UAS_MAX_STREAMS); p = uas->status3[st->stream]; uas->status3[st->stream] = NULL; } else { @@ -383,8 +384,14 @@ static void usb_uas_send_status_bh(void *opaque) static void usb_uas_queue_status(UASDevice *uas, UASStatus *st, int length) { - USBPacket *p = uas_using_streams(uas) ? - uas->status3[st->stream] : uas->status2; + USBPacket *p; + + if (uas_using_streams(uas)) { + assert(st->stream <= UAS_MAX_STREAMS); + p = uas->status3[st->stream]; + } else { + p = uas->status2; + } st->length += length; QTAILQ_INSERT_TAIL(&uas->results, st, next); @@ -700,14 +707,22 @@ static void usb_uas_command(UASDevice *uas, uas_iu *iu) uint16_t tag = be16_to_cpu(iu->hdr.tag); size_t cdb_len = sizeof(iu->command.cdb) + iu->command.add_cdb_length; + if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { + /* + * Our status delivery only works with valid tags, so in case the + * stream ID is out of bounds, we have to return immediately here + * without sending a fake sense_code_INVALID_TAG to the guest. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "invalid tag 0x%x for USB UAS command\n", tag); + return; + } + if (iu->command.add_cdb_length > 0) { qemu_log_mask(LOG_UNIMP, "additional adb length not yet supported\n"); goto unsupported_len; } - if (uas_using_streams(uas) && tag > UAS_MAX_STREAMS) { - goto invalid_tag; - } req = usb_uas_find_request(uas, tag); if (req) { goto overlapped_tag; @@ -744,10 +759,6 @@ unsupported_len: usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_PARAM_VALUE); return; -invalid_tag: - usb_uas_queue_fake_sense(uas, tag, sense_code_INVALID_TAG); - return; - overlapped_tag: usb_uas_queue_fake_sense(uas, tag, sense_code_OVERLAPPED_COMMANDS); return; From 9b899ddb2706150ee1e5d0150a11bb021377ecb8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 21 Jul 2026 09:02:16 +0200 Subject: [PATCH 136/860] hw/ide/core: Fix possible crash via NULL pointer in ide_cancel_dma_sync() ide_cancel_dma_sync() is called with a "IDEState *s" for one of the two IDE drives on a bus (primary or secondary drive) to cancel all pending DMA transfers on the drive. The code then checks s->bus->dma->aiocb to see whether there is any IO in flight on the *bus* and then calls blk_drain(s->blk) to wait for its completion. However, s->bus->dma->aiocb might belong to the other drive on the bus, and if there is no disk attached to the current drive, s->blk is NULL. Since blk_drain() does not check its parameter for a NULL pointer, QEMU can crash in such a case. To fix the problem, we have to check that "blk" is not NULL before calling blk_drain(). And we have to call blk_drain() for both drives, otherwise the assert(s->bus->dma->aiocb == NULL) statement after the blk_drain() might trigger if the IO in flight belongs to the the other drive. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/905 Reported-by: Alexander Bulekov Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4052 Reported-by: dong ling Signed-off-by: Thomas Huth Message-ID: <20260721070216.82984-1-thuth@redhat.com> --- hw/ide/core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 4c1ee19d8e..fb9bf11b45 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -741,10 +741,17 @@ void ide_cancel_dma_sync(IDEState *s) * In the future we'll be able to safely cancel the I/O if the * whole DMA operation will be submitted to disk with a single * aio operation with preadv/pwritev. + * + * Note: s->bus->dma->aiocb might belong to the adjacent IDEState, + * so we have to drain both drives to get it cleared. */ if (s->bus->dma->aiocb) { trace_ide_cancel_dma_sync_remaining(); - blk_drain(s->blk); + for (int i = 0; i < 2; i++) { + if (s->bus->ifs[i].blk) { + blk_drain(s->bus->ifs[i].blk); + } + } assert(s->bus->dma->aiocb == NULL); } } From 68809288c7e5bf2a9a9edeb55af8c368f52442ad Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 21 Jul 2026 20:51:40 +0200 Subject: [PATCH 137/860] hw/usb/core: Avoid possible assert() in do_parameter() --> usb_packet_copy() usb_packet_copy() uses assert(p->actual_length + bytes <= iov->size) to make sure that there is enough space in the the iov. This assert() can be triggered from do_parameter() if the guest programs the XHCI in a weird way. Avoid the hard error by checking for the condition in do_parameter() first and signalling a USB_RET_STALL to the guest, just like it is done for another error condition here already some lines earlier. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3746 Reported-by: Yunhe Wang Signed-off-by: Thomas Huth Message-ID: <20260721185140.247775-1-thuth@redhat.com> --- hw/usb/core.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/usb/core.c b/hw/usb/core.c index d71204c5c8..43653c26ca 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "hw/usb/usb.h" #include "qemu/iov.h" +#include "qemu/log.h" #include "trace.h" void usb_pick_speed(USBPort *port) @@ -288,6 +289,15 @@ static void do_parameter(USBDevice *s, USBPacket *p) p->status = USB_RET_STALL; return; } + if ((p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_IN) && + setup_len > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: setup state param length %u > iov size %zu\n", + setup_len, p->iov.size); + p->status = USB_RET_STALL; + return; + } + s->setup_len = setup_len; if (p->pid == USB_TOKEN_OUT) { From 92abc3c51ebb3223a6fdb5bd3414b2943d7653b2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 24 Jul 2026 13:09:33 +0200 Subject: [PATCH 138/860] hw/usb/hcd-xhci: Check return value of xhci_xfer_create_sgl() for errors xhci_xfer_create_sgl() can fail if a guest programmed the XHCI in a weird way. The current code ignores this error, and this triggers an assert() shortly afterwards: hw/usb/core.c:612: usb_packet_copy: Assertion `p->actual_length + bytes <= iov->size' failed. Fix it by handling the error correctly (i.e. return with an error to the caller). While we're at it, change the DPRINTF statements in xhci_xfer_create_sgl() into proper qemu_log_mask() statements, so we have a better way to detect this situation. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3786 Reported-by: Feifan Qian Reviewed-by: Peter Maydell Signed-off-by: Thomas Huth Message-ID: <20260724110933.629791-1-thuth@redhat.com> --- hw/usb/hcd-xhci.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 569386b8cf..d342aa2739 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1464,7 +1464,8 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) switch (TRB_TYPE(*trb)) { case TR_DATA: if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) { - DPRINTF("xhci: data direction mismatch for TR_DATA\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: data direction mismatch for TR_DATA\n"); goto err; } /* fallthrough */ @@ -1474,7 +1475,8 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) chunk = trb->status & 0x1ffff; if (trb->control & TRB_TR_IDT) { if (chunk > 8 || in_xfer) { - DPRINTF("xhci: invalid immediate data TRB\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "xhci: invalid immediate data TRB\n"); goto err; } qemu_sglist_add(&xfer->sgl, trb->addr, chunk); @@ -1617,7 +1619,9 @@ static int xhci_setup_packet(XHCITransfer *xfer) } } - xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN); /* Also sets int_req */ + if (xhci_xfer_create_sgl(xfer, dir == USB_TOKEN_IN) < 0) { /* Also sets int_req */ + return -1; + } usb_packet_setup(&xfer->packet, dir, ep, xfer->streamid, xfer->trbs[0].addr, false, xfer->int_req); if (usb_packet_map(&xfer->packet, &xfer->sgl)) { From 76dcc0832c5caac25ac887f235b547ed947b5734 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 19 Jul 2026 20:35:13 +0900 Subject: [PATCH 139/860] hw/display/virtio-gpu: Remove the bytes_pp field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio_gpu_do_set_scanout() validates the stride field of struct virtio_gpu_framebuffer against the bytes_pp field, but bytes_pp in the migration stream may be inconsistent with the format field, which pixman_image_create_bits() uses when it accesses the framebuffer. That validation is therefore incomplete. To avoid the trouble of synchronizing the two fields, remove bytes_pp, and always derive its value from format. Removing bytes_pp is safe because no released version of QEMU uses its migrated value. Fixes: 7b5574225429 ("hw/display: check frame buffer can hold blob") Cc: qemu-stable@nongnu.org Reviewed-by: Dmitry Osipenko Reviewed-by: Philippe Mathieu-Daudé [ Marc-André - fix rebase conflict ] Signed-off-by: Marc-André Lureau Signed-off-by: Akihiko Odaki Message-ID: <20260719-bpp-v1-1-9b91946d6cf3@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 27 +++++++++++++++++---------- include/hw/virtio/virtio-gpu.h | 1 - 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 718ba30392..eac039c3c3 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -617,6 +617,11 @@ void virtio_gpu_update_scanout(VirtIOGPU *g, scanout->fb = *fb; } +static uint32_t virtio_gpu_format_bytes_pp(pixman_format_code_t format) +{ + return DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); +} + static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, @@ -625,6 +630,7 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t *error) { struct virtio_gpu_scanout *scanout; + uint32_t bytes_pp = virtio_gpu_format_bytes_pp(fb->format); uint8_t *data; scanout = &g->parent_obj.scanout[scanout_id]; @@ -646,10 +652,10 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, return false; } - if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) { + if (fb->stride < (uint64_t)fb->width * bytes_pp) { qemu_log_mask(LOG_GUEST_ERROR, "%s: stride %u too small for width %u at %u bpp\n", - __func__, fb->stride, fb->width, fb->bytes_pp); + __func__, fb->stride, fb->width, bytes_pp); *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return false; } @@ -720,6 +726,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, struct virtio_gpu_simple_resource *res; struct virtio_gpu_framebuffer fb = { 0 }; struct virtio_gpu_set_scanout ss; + uint32_t bytes_pp; VIRTIO_GPU_FILL_CMD(ss); virtio_gpu_bswap_32(&ss, sizeof(ss)); @@ -745,11 +752,11 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, } fb.format = pixman_image_get_format(res->image); - fb.bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb.format), 8); + bytes_pp = virtio_gpu_format_bytes_pp(fb.format); fb.width = pixman_image_get_width(res->image); fb.height = pixman_image_get_height(res->image); fb.stride = pixman_image_get_stride(res->image); - fb.offset = ss.r.x * fb.bytes_pp + ss.r.y * fb.stride; + fb.offset = ss.r.x * bytes_pp + ss.r.y * fb.stride; virtio_gpu_do_set_scanout(g, ss.scanout_id, &fb, res, &ss.r, &cmd->error); @@ -760,6 +767,7 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, uint64_t blob_size) { uint64_t fbend; + uint32_t bytes_pp; fb->format = virtio_gpu_get_pixman_format(ss->format); if (!fb->format) { @@ -769,15 +777,15 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, return false; } - fb->bytes_pp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(fb->format), 8); + bytes_pp = virtio_gpu_format_bytes_pp(fb->format); fb->width = ss->width; fb->height = ss->height; fb->stride = ss->strides[0]; - if (fb->stride < (uint64_t)fb->width * fb->bytes_pp) { + if (fb->stride < (uint64_t)fb->width * bytes_pp) { qemu_log_mask(LOG_GUEST_ERROR, "%s: stride %u too small for width %u at %u bpp\n", - __func__, fb->stride, fb->width, fb->bytes_pp); + __func__, fb->stride, fb->width, bytes_pp); return false; } @@ -788,7 +796,7 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, return false; } - fb->offset = ss->offsets[0] + ss->r.x * fb->bytes_pp + ss->r.y * fb->stride; + fb->offset = ss->offsets[0] + ss->r.x * bytes_pp + ss->r.y * fb->stride; fbend = fb->offset; fbend += (uint64_t) fb->stride * ss->r.height; @@ -1238,8 +1246,7 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = { VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout), VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout, scanout_vmstate_after_v2), - VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout, - scanout_vmstate_after_v2), + VMSTATE_UNUSED_TEST(scanout_vmstate_after_v2, 4), VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout, scanout_vmstate_after_v2), VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout, diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index b9bad27c97..2f60c72078 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -66,7 +66,6 @@ struct virtio_gpu_simple_resource { struct virtio_gpu_framebuffer { pixman_format_code_t format; - uint32_t bytes_pp; uint32_t width, height; uint32_t stride; uint32_t offset; From 1c1232c2ee0798e5f8446200005f045a153a5648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 7 Jul 2026 11:38:36 +0400 Subject: [PATCH 140/860] hw/display/vhost-user-gpu: validate message payload sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A malicious or buggy vhost-user-gpu backend can send messages with undersized payloads, leading to out-of-bounds reads when the handler accesses struct fields beyond the allocated buffer. However, vhost-user-gpu is considered trusted by QEMU by design (it has access to shared memory etc). Add a centralized minimum payload size check in vhost_user_gpu_chr_read() that rejects messages before dispatch, and a per-pixel bounds check in the VHOST_USER_GPU_UPDATE handler to ensure the variable-length data covers the declared width x height. Fixes: 267f66465 ("hw/display: add vhost-user-vga & gpu-pci") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3866 Reported-by: Feifan Qian Reviewed-by: Thomas Huth Signed-off-by: Marc-André Lureau --- hw/display/vhost-user-gpu.c | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 57360898ca..cd684d6363 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -119,6 +119,31 @@ static VhostUserGpuMsg m __attribute__ ((unused)); static void vhost_user_gpu_update_blocked(VhostUserGPU *g, bool blocked); +static size_t +vhost_user_gpu_min_payload_size(VhostUserGpuRequest request) +{ + switch (request) { + case VHOST_USER_GPU_CURSOR_POS: + case VHOST_USER_GPU_CURSOR_POS_HIDE: + return sizeof(VhostUserGpuCursorPos); + case VHOST_USER_GPU_CURSOR_UPDATE: + return sizeof(VhostUserGpuCursorUpdate); + case VHOST_USER_GPU_GET_EDID: + return sizeof(VhostUserGpuEdidRequest); + case VHOST_USER_GPU_SCANOUT: + return sizeof(VhostUserGpuScanout); + case VHOST_USER_GPU_DMABUF_SCANOUT: + return sizeof(VhostUserGpuDMABUFScanout); + case VHOST_USER_GPU_DMABUF_SCANOUT2: + return sizeof(VhostUserGpuDMABUFScanout2); + case VHOST_USER_GPU_DMABUF_UPDATE: + case VHOST_USER_GPU_UPDATE: + return sizeof(VhostUserGpuUpdate); + default: + return 0; + } +} + static void vhost_user_gpu_handle_cursor(VhostUserGPU *g, VhostUserGpuMsg *msg) { @@ -322,6 +347,14 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) if (m->scanout_id >= g->parent_obj.conf.max_outputs) { break; } + + if ((uint64_t)m->width * m->height > + (msg->size - sizeof(VhostUserGpuUpdate)) / sizeof(uint32_t)) { + error_report("vhost-user-gpu: update payload too small" + " for %ux%u", m->width, m->height); + break; + } + s = &g->parent_obj.scanout[m->scanout_id]; con = s->con; pixman_image_t *image = @@ -396,6 +429,11 @@ vhost_user_gpu_chr_read(void *opaque) msg->flags = flags; msg->size = size; + if (size < vhost_user_gpu_min_payload_size(request)) { + error_report("vhost-user-gpu: message %d payload too small", request); + goto end; + } + if (request == VHOST_USER_GPU_CURSOR_UPDATE || request == VHOST_USER_GPU_CURSOR_POS || request == VHOST_USER_GPU_CURSOR_POS_HIDE) { From efa7244b2a0bd5e40ec334dbec4a09af0134d91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Jul 2026 19:26:23 +0400 Subject: [PATCH 141/860] ui/vnc: remove redundant rows computation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "rows" was already computed in an earlier commit 3543c2b855 ("ui/vnc: fix OOB write in vnc_refresh_lossy_rect"). Fixes: e650e4fe0f ("ui/vnc: fix out-of-bounds write in lossy refresh dirty marking") Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau --- ui/vnc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/vnc.c b/ui/vnc.c index 24aa40f730..656768f9c9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3016,7 +3016,6 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y, y = QEMU_ALIGN_DOWN(y, VNC_STAT_RECT); x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); - rows = MIN(VNC_STAT_RECT, height - y); rows = MIN(VNC_STAT_RECT, height - y); if (rows <= 0) { From 4727cc883b7e81d2c30b9801af54482d65b84292 Mon Sep 17 00:00:00 2001 From: Haotian Jiang Date: Mon, 20 Jul 2026 10:48:55 +0800 Subject: [PATCH 142/860] hw/display/qxl: unregister vm_change_state handler and BHs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qxl_realize_common() registers a vm_change_state handler via qemu_add_vm_change_state_handler() and creates three bottom halves (update_irq, update_area_bh, cursor_bh), but none are ever cleaned up. The return value of qemu_add_vm_change_state_handler() is discarded, so the handler is never removed from the global list, and there is no PCIDeviceClass.exit callback to delete the BHs. When a secondary QXL device (hotpluggable by default) is hot-unplugged via device_del, the PCIQXLDevice memory is freed but the vm_state handler and BH entries remain with dangling opaque pointers. On the next VM state change (stop/cont/migrate) or BH dispatch, the callback dereferences freed memory, causing a use-after-free. Fix this by storing the VMChangeStateEntry returned by qemu_add_vm_change_state_handler() and adding a qxl_exit() callback that deletes the vm_state handler, all three BHs, and the guest_surfaces.cmds allocation before the device memory is freed. Fixes: a19cbfb34642 ("spice: add qxl device") Fixes: CVE-2026-63322 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3607 Signed-off-by: Haotian Jiang Cc: qemu-stable@nongnu.org [ Marc-André - tweak commit message, add TODO ] Reviewed-by: Marc-André Lureau Message-ID: <20260720024855.3757499-1-jianghaotian.sunday@gmail.com> --- hw/display/qxl.c | 16 +++++++++++++++- hw/display/qxl.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 3df796175c..b7d871b9ee 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2203,7 +2203,8 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) error_report_err(err); } - qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); + qxl->vmstate_handler = + qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); qxl->update_irq = qemu_bh_new_guarded(qxl_update_irq_bh, qxl, &DEVICE(qxl)->mem_reentrancy_guard); @@ -2475,6 +2476,18 @@ static const Property qxl_properties[] = { DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), }; +static void qxl_exit(PCIDevice *dev) +{ + PCIQXLDevice *qxl = PCI_QXL(dev); + + /* TODO: complete cleanup, error paths etc */ + g_clear_pointer(&qxl->vmstate_handler, qemu_del_vm_change_state_handler); + g_clear_pointer(&qxl->update_irq, qemu_bh_delete); + g_clear_pointer(&qxl->update_area_bh, qemu_bh_delete); + g_clear_pointer(&qxl->ssd.cursor_bh, qemu_bh_delete); + g_clear_pointer(&qxl->guest_surfaces.cmds, g_free); +} + static void qxl_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2482,6 +2495,7 @@ static void qxl_pci_class_init(ObjectClass *klass, const void *data) k->vendor_id = REDHAT_PCI_VENDOR_ID; k->device_id = QXL_DEVICE_ID_STABLE; + k->exit = qxl_exit; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); device_class_set_legacy_reset(dc, qxl_reset_handler); dc->vmsd = &qxl_vmstate; diff --git a/hw/display/qxl.h b/hw/display/qxl.h index ad8a912878..48d664f777 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -83,6 +83,7 @@ struct PCIQXLDevice { /* thread signaling */ QEMUBH *update_irq; + VMChangeStateEntry *vmstate_handler; /* ram pci bar */ QXLRam *ram; From b28778131fad008fcb79204874d926c2433e518f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 9 Jul 2026 15:13:15 +0400 Subject: [PATCH 143/860] net/colo: fix g_hash_table_destroy assertion on uninitialized filter colo_rewriter_cleanup() unconditionally destroys connection_track_table, but the table is only allocated in colo_rewriter_setup(). When the filter-rewriter object is finalized without having been set up (e.g. during qom-test property enumeration), the pointer is NULL and g_hash_table_destroy() fires an assertion. Use g_clear_pointer() to skip the destroy when the table was never initialized. Signed-off-by: Marc-Andre Lureau Reviewed-by: Zhang Chen Message-ID: <20260709111315.1108185-1-marcandre.lureau@redhat.com> --- net/filter-rewriter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index cdf85aa5ee..c85d180eb9 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -373,7 +373,7 @@ static void colo_rewriter_cleanup(NetFilterState *nf) g_free(s->incoming_queue); } - g_hash_table_destroy(s->connection_track_table); + g_clear_pointer(&s->connection_track_table, g_hash_table_destroy); } static void colo_rewriter_setup(NetFilterState *nf, Error **errp) From 415f2759d7b3294e15c783849a08deb26885cabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 21 May 2026 18:54:51 +0400 Subject: [PATCH 144/860] target/i386/sev: fix MemoryRegion reference leaks in gpa2hva callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpa2hva() returns a referenced MemoryRegion via its p_mr out-parameter (inherited from memory_region_find()), but both SEV callers were failing to release it. Fixes: c7f7e6970d3b ("sev: add sev-inject-launch-secret") Reviewed-by: Peter Xu Reviewed-by: Zhao Liu Signed-off-by: Marc-André Lureau Message-ID: <20260521145451.1831984-1-marcandre.lureau@redhat.com> --- include/system/memory.h | 2 ++ target/i386/sev.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/system/memory.h b/include/system/memory.h index 47a0e06fbf..2192fc9bdc 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -1085,6 +1085,8 @@ void memory_region_ref(MemoryRegion *mr); */ void memory_region_unref(MemoryRegion *mr); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(MemoryRegion, memory_region_unref) + /** * memory_region_init_io: Initialize an I/O memory region. * diff --git a/target/i386/sev.c b/target/i386/sev.c index debe6344ab..d7dcefc8ec 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1569,7 +1569,7 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, OvmfSevMetadataDesc *desc; int type, ret, i; void *hva; - MemoryRegion *mr = NULL; + g_autoptr(MemoryRegion) mr = NULL; for (i = 0; i < metadata->num_desc; i++) { desc = &metadata->descs[i]; @@ -2063,7 +2063,7 @@ int sev_inject_launch_secret(const char *packet_hdr, const char *secret, int error, ret = 1; void *hva; gsize hdr_sz = 0, data_sz = 0; - MemoryRegion *mr = NULL; + g_autoptr(MemoryRegion) mr = NULL; SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_common) { From 8ac0a1c7b98e11693db8fc6b390cc892b6e3e6c8 Mon Sep 17 00:00:00 2001 From: Haotian Jiang Date: Mon, 20 Jul 2026 15:15:56 +0800 Subject: [PATCH 145/860] virtio-gpu: fix NULL deref in rutabaga set_scanout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rutabaga_cmd_set_scanout() checks scanout_id < VIRTIO_GPU_MAX_SCANOUTS (16), but does not check scanout_id < conf.max_outputs like the base class (virtio-gpu.c) and virgl backend (virtio-gpu-virgl.c) do. With the default max_outputs=1, virtio_gpu_base_device_realize only initializes scanout[0].con. A guest submitting SET_SCANOUT with scanout_id >= 1 takes the con=NULL path, and qemu_console_set_surface(NULL, NULL) dereferences con->ds, crashing QEMU. Replace VIRTIO_GPU_MAX_SCANOUTS with vb->conf.max_outputs in the CHECK, since realization already ensures max_outputs <= VIRTIO_GPU_MAX_SCANOUTS. Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3897 Cc: qemu-stable@nongnu.org Message-ID: <20260720071556.106003-1-jianghaotian.sunday@gmail.com> Reviewed-by: Dmitry Osipenko Reviewed-by: Marc-André Lureau Signed-off-by: Haotian Jiang --- hw/display/virtio-gpu-rutabaga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index 4d7d7b2459..6af5c2514d 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -302,7 +302,7 @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id, ss.r.width, ss.r.height, ss.r.x, ss.r.y); - CHECK(ss.scanout_id < VIRTIO_GPU_MAX_SCANOUTS, cmd); + CHECK(ss.scanout_id < vb->conf.max_outputs, cmd); scanout = &vb->scanout[ss.scanout_id]; if (ss.resource_id == 0) { From 521500bd803b3c2c619e52ce423ed039e91310f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 1 Jul 2026 16:21:35 +0400 Subject: [PATCH 146/860] docs/hyperv: fix misleading hv-crash shutdown description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation stated that writing to HV_X64_MSR_CRASH_CTL unconditionally causes the guest to shutdown. In reality, it triggers qemu_system_guest_panicked() via KVM_SYSTEM_EVENT_CRASH and the resulting action depends on the generic panic action policy (-action panic=...), which defaults to shutdown. Reviewed-by: Vitaly Kuznetsov Signed-off-by: Marc-André Lureau Message-ID: <20260701122135.3168729-1-marcandre.lureau@redhat.com> --- docs/system/i386/hyperv.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/system/i386/hyperv.rst b/docs/system/i386/hyperv.rst index 1c1de77feb..e1babffb0f 100644 --- a/docs/system/i386/hyperv.rst +++ b/docs/system/i386/hyperv.rst @@ -71,8 +71,11 @@ Existing enlightenments by the guest when it crashes, HV_X64_MSR_CRASH_P0..HV_X64_MSR_CRASH_P5 MSRs contain additional crash information. This information is outputted in QEMU log and through QAPI. - Note: unlike under genuine Hyper-V, write to HV_X64_MSR_CRASH_CTL causes guest - to shutdown. This effectively blocks crash dump generation by Windows. + Note: unlike under genuine Hyper-V, write to HV_X64_MSR_CRASH_CTL triggers + ``qemu_system_guest_panicked()`` via ``KVM_SYSTEM_EVENT_CRASH`` and the + resulting action depends on the ``-action panic=...`` policy (default: + ``shutdown``). With the default action, this effectively blocks crash dump + generation by Windows. ``hv-time`` Enables two Hyper-V-specific clocksources available to the guest: MSR-based @@ -304,8 +307,9 @@ currently implemented Hyper-V enlightenments with the following exceptions: ``hv-version-id-snumber`` can be left unchanged, guests are not supposed to behave differently when different Hyper-V version is presented to them. - ``hv-crash`` must only be enabled if the crash information is consumed via - QAPI by higher levels of the virtualization stack. Enabling this feature - effectively prevents Windows from creating dumps upon crashes. + QAPI by higher levels of the virtualization stack. With the default + ``-action panic=shutdown`` policy, enabling this feature effectively + prevents Windows from creating dumps upon crashes. - ``hv-reenlightenment`` can only be used on hardware which supports TSC scaling or when guest migration is not needed. - ``hv-spinlocks`` should be set to e.g. 0xfff when host CPUs are overcommited From 0fa3e1823b9b9ae1fc1ea0d290849578907a770b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 25 Jul 2026 16:12:51 +0900 Subject: [PATCH 147/860] hw/display/virtio-gpu: Fix empty blob discrimination MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discriminating blobs by checking whether blob_size is nonzero fails for empty blobs. Identify 2D resources by their non-NULL image instead. Fixes: bdd53f739273 ("virtio-gpu: Update cursor data using blob") Fixes: f66767f75c9c ("virtio-gpu: add virtio-gpu/blob vmstate subsection") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260725-image-v1-1-4698a805afde@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index eac039c3c3..add60926f3 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -56,18 +56,18 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, return; } - if (res->blob_size) { - if (res->blob_size < (s->current_cursor->width * - s->current_cursor->height * 4)) { - return; - } - data = res->blob; - } else { + if (res->image) { if (pixman_image_get_width(res->image) != s->current_cursor->width || pixman_image_get_height(res->image) != s->current_cursor->height) { return; } data = pixman_image_get_data(res->image); + } else { + if (res->blob_size < (s->current_cursor->width * + s->current_cursor->height * 4)) { + return; + } + data = res->blob; } pixels = s->current_cursor->width * s->current_cursor->height; @@ -1286,7 +1286,7 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { - if (res->blob_size) { + if (!res->image) { continue; } qemu_put_be32(f, res->resource_id); @@ -1437,7 +1437,7 @@ static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size, assert(QTAILQ_EMPTY(&g->cmdq)); QTAILQ_FOREACH(res, &g->reslist, next) { - if (!res->blob_size) { + if (res->image) { continue; } assert(!res->image); From 4ae1c5c7d6f33d1e9c030ec4d24ab272609188cc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 25 Jul 2026 16:11:46 +0900 Subject: [PATCH 148/860] hw/display/virtio-gpu: Initialize blob mapping for ATTACH_BACKING MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is necessary for blob cursor and scanout. Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260725-backing-v1-1-5b584bf7df5a@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index add60926f3..4d46a4eb10 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -992,6 +992,10 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; } + + if (!res->image) { + virtio_gpu_init_udmabuf(res); + } } static void From 8dc8449a678f56ea0746e1c002f3c671ef9c456d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 25 Jul 2026 16:14:21 +0900 Subject: [PATCH 149/860] hw/display/virtio-gpu: Avoid leaking migration blocker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio_gpu_base_device_realize() leaks a migration blocker if a check of the output list fails after adding one. Perform the check before adding a migration blocker to avoid the leak. This also simplifies the code by merging two loops. Fixes: d3a4969dc5ac ("Support per-head resolutions with virtio-gpu") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260725-virgl-v1-1-58bb51e60da5@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu-base.c | 37 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index a68b184829..946e56b42f 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -195,6 +195,11 @@ virtio_gpu_base_device_realize(DeviceState *qdev, return false; } + g->enabled_output_bitmask = 1; + + g->req_state[0].width = g->conf.xres; + g->req_state[0].height = g->conf.yres; + for (output_idx = 0, node = g->conf.outputs; node; output_idx++, node = node->next) { if (output_idx == g->conf.max_outputs) { @@ -207,6 +212,17 @@ virtio_gpu_base_device_realize(DeviceState *qdev, node->value->name, EDID_NAME_MAX_LENGTH); return false; } + if (node->value->has_xres != node->value->has_yres) { + error_setg(errp, + "must set both outputs[%zd].xres and outputs[%zd].yres", + output_idx, output_idx); + return false; + } + if (node->value->has_xres && node->value->has_yres) { + g->enabled_output_bitmask |= (1 << output_idx); + g->req_state[output_idx].width = node->value->xres; + g->req_state[output_idx].height = node->value->yres; + } } if (virtio_gpu_virgl_enabled(g->conf)) { @@ -229,27 +245,6 @@ virtio_gpu_base_device_realize(DeviceState *qdev, virtio_add_queue(vdev, 16, cursor_cb); } - g->enabled_output_bitmask = 1; - - g->req_state[0].width = g->conf.xres; - g->req_state[0].height = g->conf.yres; - - for (output_idx = 0, node = g->conf.outputs; - node && output_idx < g->conf.max_outputs; - output_idx++, node = node->next) { - if (node->value->has_xres != node->value->has_yres) { - error_setg(errp, - "must set both outputs[%zd].xres and outputs[%zd].yres", - output_idx, output_idx); - return false; - } - if (node->value->has_xres && node->value->has_yres) { - g->enabled_output_bitmask |= (1 << output_idx); - g->req_state[output_idx].width = node->value->xres; - g->req_state[output_idx].height = node->value->yres; - } - } - g->hw_ops = &virtio_gpu_ops; for (i = 0; i < g->conf.max_outputs; i++) { g->scanout[i].con = From 5c9ef661a60ad737c61f4a872226376b5f2d424a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 25 Jul 2026 16:15:32 +0900 Subject: [PATCH 150/860] hw/display/virtio-gpu: Block Rutabaga migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The migration stream encoding is not defined for Rutabaga yet. Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260725-rutabaga-v1-1-4832b56e679d@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu-rutabaga.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index 6af5c2514d..8e6c7a0d5d 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -9,6 +9,7 @@ #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-pixman.h" #include "hw/virtio/virtio-iommu.h" +#include "migration/blocker.h" #include #include @@ -1078,6 +1079,7 @@ static void virtio_gpu_rutabaga_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) { + ERRP_GUARD(); uint32_t num_capsets; VirtIOGPUBase *bdev = VIRTIO_GPU_BASE(qdev); VirtIOGPU *gpudev = VIRTIO_GPU(qdev); @@ -1087,12 +1089,17 @@ static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) return; #endif - if (!virtio_gpu_rutabaga_init(gpudev, errp)) { + error_setg(&bdev->migration_blocker, "rutabaga is not yet migratable"); + if (migrate_add_blocker(&bdev->migration_blocker, errp) < 0) { return; } + if (!virtio_gpu_rutabaga_init(gpudev, errp)) { + goto fail; + } + if (!virtio_gpu_rutabaga_get_num_capsets(gpudev, &num_capsets, errp)) { - return; + goto fail; } bdev->conf.flags |= (1 << VIRTIO_GPU_FLAG_RUTABAGA_ENABLED); @@ -1101,6 +1108,12 @@ static void virtio_gpu_rutabaga_realize(DeviceState *qdev, Error **errp) bdev->virtio_config.num_capsets = num_capsets; virtio_gpu_device_realize(qdev, errp); + if (!*errp) { + return; + } + +fail: + migrate_del_blocker(&bdev->migration_blocker); } static const Property virtio_gpu_rutabaga_properties[] = { From d541d7dc2fb4ee0d86ab7b4a7c466f0e8211d9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 23 Jul 2026 12:07:00 +0400 Subject: [PATCH 151/860] hw/display/virtio-gpu-rutabaga: zero-init capset info response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rutabaga_cmd_get_capset_info() only fills in capset_id, capset_max_version and capset_max_size before sending the response to the guest. The remaining fields of struct virtio_gpu_resp_capset_info, including hdr.fence_id, hdr.ctx_id and hdr.ring_idx, are left with stack garbage and leaked to the guest, including host pointers useful for an ASLR bypass. Zero the response first, matching virgl_cmd_get_capset_info(). Not a real risk thanks to -ftrivial-auto-var-init=zero, but only with gcc >= 12 or clang >= 16. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3609 Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream") Reported-by: Haotian Jiang Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau --- hw/display/virtio-gpu-rutabaga.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index 8e6c7a0d5d..e28aad94ee 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -556,6 +556,8 @@ rutabaga_cmd_get_capset_info(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) VIRTIO_GPU_FILL_CMD(info); + memset(&resp, 0, sizeof(resp)); + result = rutabaga_get_capset_info(vr->rutabaga, info.capset_index, &resp.capset_id, &resp.capset_max_version, &resp.capset_max_size); From fdad10190af75010558bfc6d00706537b33b8346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 26 Jul 2026 20:59:50 +0400 Subject: [PATCH 152/860] hw/cxl: fix invalid free on early return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/devel/style.rst: * Variables declared with g_auto* MUST always be initialized, otherwise the cleanup function will use uninitialized stack memory Fixes: 680935c9a6ff ("hw/cxl: Add a performant (and correct) path for the non interleaved cases") Reviewed-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau --- hw/cxl/cxl-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index 7e744312f1..eba13c9e7c 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -279,7 +279,7 @@ static void cxl_fmws_direct_passthrough_setup(CXLDirectPTState *state, MemoryRegion *mr = NULL; uint64_t vmr_size = 0, pmr_size = 0, offset = 0; MemoryRegion *direct_mr; - g_autofree char *direct_mr_name; + g_autofree char *direct_mr_name = NULL; unsigned int idx = state->hdm_decoder_idx; if (ct3d->hostvmem) { From 5e8d44971ed362c5f517547ed24d82b101f96171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 21 Jul 2026 23:46:48 +0400 Subject: [PATCH 153/860] hw/hexagon: fix machine->fdt leak in qom-test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virt_instance_init() built the FDT unconditionally at QOM instance-init time, so simply instantiating the object (e.g. via qom-test's introspection, without ever realizing the machine) leaked the 1MB FDT blob: machine_finalize() does not free machine->fdt. Other boards (arm/virt, riscv/virt, ...) build the FDT lazily from their MachineClass::init callback, which only runs when the machine is actually selected to boot. Do the same here by moving create_fdt() into virt_init(). Fixes: 88a8bc7f43ff ("hw/hexagon: Define hexagon "virt" machine") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau --- hw/hexagon/virt.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index a3638998b8..b750472502 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -175,13 +175,6 @@ static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms) -static void virt_instance_init(Object *obj) -{ - HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(obj); - - create_fdt(vms); -} - void hexagon_load_fdt(const HexagonVirtMachineState *vms) { MachineState *ms = MACHINE(vms); @@ -242,6 +235,7 @@ static void virt_init(MachineState *ms) DeviceState *cpu0; int32_t clk_phandle; + create_fdt(vms); qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline); vms->sys = get_system_memory(); @@ -341,7 +335,6 @@ static const TypeInfo virt_machine_types[] = { { .parent = TYPE_HEXAGON_COMMON_MACHINE, .instance_size = sizeof(HexagonVirtMachineState), .class_init = virt_class_init, - .instance_init = virt_instance_init, } }; DEFINE_TYPES(virt_machine_types) From 6b47b70185c35a902ad6e93d0be2f942d6dce07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 26 Jul 2026 20:18:57 +0400 Subject: [PATCH 154/860] block/blkio: fix compiler false-positive warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under some optimization, gcc produces a false-positive: ../block/blkio.c: In function ‘blkio_co_getlength’: ../block/blkio.c:943:8: error: ‘ret’ may be used uninitialized [-Werror=maybe-uninitialized] 943 | if (ret < 0) { | ^ Replace WITH_QEMU_LOCK_GUARD with the simpler QEMU_LOCK_GUARD. Suggested-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau --- block/blkio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/block/blkio.c b/block/blkio.c index fb8bec27d7..008320bf20 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -937,9 +937,8 @@ static int64_t coroutine_fn blkio_co_getlength(BlockDriverState *bs) uint64_t capacity; int ret; - WITH_QEMU_LOCK_GUARD(&s->blkio_lock) { - ret = blkio_get_uint64(s->blkio, "capacity", &capacity); - } + QEMU_LOCK_GUARD(&s->blkio_lock); + ret = blkio_get_uint64(s->blkio, "capacity", &capacity); if (ret < 0) { return -ret; } From 8c57370d189f06e766804704e470252d76cbe30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 19 Jul 2026 23:28:39 +0400 Subject: [PATCH 155/860] monitor: annotate monitor_qmp_dispatcher_pop_any() as coroutine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function calls qemu_coroutine_yield(), and is called from monitor_qmp_dispatcher_co(). Fixes: 60f4f62efeb ("monitor: extract request dequeuing to a new function") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Signed-off-by: Marc-André Lureau --- monitor/qmp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monitor/qmp.c b/monitor/qmp.c index 223e0643c2..338d37cb7e 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -342,7 +342,8 @@ static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) return req_obj; } -static QMPRequest *monitor_qmp_dispatcher_pop_any(void) +static QMPRequest * coroutine_fn +monitor_qmp_dispatcher_pop_any(void) { while (true) { /* From aea4af8c515fbf7be50e2cb94e5fd31307b1b3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 19 Jul 2026 23:30:24 +0400 Subject: [PATCH 156/860] migration: fix qemu_get_counted_string annotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 394b9407e4c ("migration: mark mixed functions that can suspend") Reviewed-by: Peter Xu Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau --- migration/qemu-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index d5a48115bd..f30e13c66d 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -830,7 +830,7 @@ uint64_t qemu_get_be64(QEMUFile *f) * else 0 * (Note a 0 length string will return 0 either way) */ -size_t coroutine_fn qemu_get_counted_string(QEMUFile *f, char buf[256]) +size_t coroutine_mixed_fn qemu_get_counted_string(QEMUFile *f, char buf[256]) { size_t len = qemu_get_byte(f); size_t res = qemu_get_buffer(f, (uint8_t *)buf, len); From 1fe1ec6773167f3f167fe8ec2c976b7bd3910d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 20 Jul 2026 11:06:52 +0400 Subject: [PATCH 157/860] io: add missing coroutine annotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 1edf0df28409 ("io: Add qio_channel_wait_cond() helper") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau --- include/io/channel.h | 4 ++-- io/channel.c | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/io/channel.h b/include/io/channel.h index 98485c9280..c90ace20d5 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -1016,8 +1016,8 @@ void qio_channel_wait(QIOChannel *ioc, * This will work with/without a coroutine context, by automatically select * the proper API to wait. */ -void qio_channel_wait_cond(QIOChannel *ioc, - GIOCondition condition); +void coroutine_mixed_fn qio_channel_wait_cond(QIOChannel *ioc, + GIOCondition condition); /** * qio_channel_set_aio_fd_handler: diff --git a/io/channel.c b/io/channel.c index 2853dadb68..620256f310 100644 --- a/io/channel.c +++ b/io/channel.c @@ -905,8 +905,9 @@ void qio_channel_wait(QIOChannel *ioc, g_main_context_unref(ctxt); } -void qio_channel_wait_cond(QIOChannel *ioc, - GIOCondition condition) +void coroutine_mixed_fn +qio_channel_wait_cond(QIOChannel *ioc, + GIOCondition condition) { if (qemu_in_coroutine()) { qio_channel_yield(ioc, condition); From a6c131217dd63b420e73175f14bbeb9a7260ebd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 19 Jul 2026 23:50:01 +0400 Subject: [PATCH 158/860] block: add missing coroutine annotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function was extracted without carrying the annotation. Fixes: 23743ab282af ("block: move commit_run loop to separate function") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf Signed-off-by: Marc-André Lureau --- block/commit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/block/commit.c b/block/commit.c index c5e3ef03a2..2d52c39594 100644 --- a/block/commit.c +++ b/block/commit.c @@ -128,8 +128,9 @@ static void commit_clean(Job *job) blk_unref(s->top); } -static int commit_iteration(CommitBlockJob *s, int64_t offset, - int64_t *requested_bytes, void *buf) +static int coroutine_fn +commit_iteration(CommitBlockJob *s, int64_t offset, + int64_t *requested_bytes, void *buf) { BlockErrorAction action; int64_t bytes = *requested_bytes; From c15ca7cf9859140918292c5a99b9acb0a3b39c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 20 Jul 2026 00:00:29 +0400 Subject: [PATCH 159/860] qcow2: remove invalid qcow2_check_refcounts calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Those DEBUG_ALLOC-guarded are dead code incorrectly calling coroutine fn from non-coroutine. Remove it. Reviewed-by: Philippe Mathieu-Daudé Fixes: 70bacc44532 ("qcow2: mark more functions as coroutine_fns and GRAPH_RDLOCK") Signed-off-by: Marc-André Lureau --- block/qcow2-snapshot.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 1e8dc48be1..8453ad73d1 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -740,12 +740,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) ROUND_UP(sn->vm_state_size, s->cluster_size), QCOW2_DISCARD_NEVER, false); -#ifdef DEBUG_ALLOC - { - BdrvCheckResult result = {0}; - qcow2_check_refcounts(bs, &result, 0); - } -#endif return 0; fail: @@ -893,12 +887,6 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) goto fail; } -#ifdef DEBUG_ALLOC - { - BdrvCheckResult result = {0}; - qcow2_check_refcounts(bs, &result, 0); - } -#endif return 0; fail: @@ -975,12 +963,6 @@ int qcow2_snapshot_delete(BlockDriverState *bs, return ret; } -#ifdef DEBUG_ALLOC - { - BdrvCheckResult result = {0}; - qcow2_check_refcounts(bs, &result, 0); - } -#endif return 0; } From 12d3fd9a0acaa009b927905b4cff069254ff04a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 20 Jul 2026 10:55:08 +0400 Subject: [PATCH 160/860] hw/9pfs: annotate V9fsTransport callbacks as coroutine_fn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All V9fsTransport callbacks are invoked exclusively from coroutine context (the v9fs_* PDU handlers). Annotate the function pointer types in V9fsTransport and all implementations (virtio and xen backends), as well as intermediate callers in 9p.c (pdu_marshal, pdu_unmarshal, v9fs_init_qiov_from_pdu, etc.). Acked-by: Christian Schoenebeck Signed-off-by: Marc-André Lureau --- hw/9pfs/9p.c | 31 +++++++++++++++++++------------ hw/9pfs/9p.h | 26 +++++++++++++++----------- hw/9pfs/virtio-9p-device.c | 24 +++++++++++++----------- hw/9pfs/xen-9p-backend.c | 34 +++++++++++++++++----------------- 4 files changed, 64 insertions(+), 51 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 1c61e6b3e7..4764d9db13 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -55,7 +55,8 @@ enum { P9ARRAY_DEFINE_TYPE(V9fsPath, v9fs_path_free); -static ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +static ssize_t coroutine_fn +pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) { ssize_t ret; va_list ap; @@ -67,7 +68,8 @@ static ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) return ret; } -static ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +static ssize_t coroutine_fn +pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) { ssize_t ret; va_list ap; @@ -1841,7 +1843,8 @@ out_nofid: pdu_complete(pdu, err); } -static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) +static int coroutine_fn +v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) { int i; ssize_t err; @@ -2363,9 +2366,10 @@ out_nofid: * The resulting QEMUIOVector has heap-allocated iovecs and must be cleaned up * with qemu_iovec_destroy(). */ -static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, - size_t skip, size_t size, - bool is_write) +static void coroutine_fn +v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, + size_t skip, size_t size, + bool is_write) { QEMUIOVector elem; struct iovec *iov; @@ -2382,8 +2386,9 @@ static void v9fs_init_qiov_from_pdu(QEMUIOVector *qiov, V9fsPDU *pdu, qemu_iovec_concat(qiov, &elem, skip, size); } -static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t max_count) +static int coroutine_fn +v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, + uint64_t off, uint32_t max_count) { ssize_t err; size_t offset = 7; @@ -2793,9 +2798,10 @@ out_nofid: pdu_complete(pdu, retval); } -static int v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, - uint64_t off, uint32_t count, - struct iovec *sg, int cnt) +static int coroutine_fn +v9fs_xattr_write(V9fsState *s, V9fsPDU *pdu, V9fsFidState *fidp, + uint64_t off, uint32_t count, + struct iovec *sg, int cnt) { int i, to_copy; ssize_t err = 0; @@ -3729,7 +3735,8 @@ out_nofid: pdu_complete(pdu, err); } -static int v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) +static int coroutine_fn +v9fs_fill_statfs(V9fsState *s, V9fsPDU *pdu, struct statfs *stbuf) { uint32_t f_type; uint32_t f_bsize; diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 1a309664f6..0e52ffbdf3 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -472,17 +472,21 @@ void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr); void v9fs_reset(V9fsState *s); struct V9fsTransport { - ssize_t (*pdu_vmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, - va_list ap); - ssize_t (*pdu_vunmarshal)(V9fsPDU *pdu, size_t offset, const char *fmt, - va_list ap); - void (*init_in_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size); - void (*init_out_iov_from_pdu)(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size); - void (*push_and_notify)(V9fsPDU *pdu); - size_t (*msize_limit)(V9fsState *s); - size_t (*response_buffer_size)(V9fsPDU *pdu); + ssize_t coroutine_fn (*pdu_vmarshal)(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap); + ssize_t coroutine_fn (*pdu_vunmarshal)(V9fsPDU *pdu, size_t offset, + const char *fmt, va_list ap); + void coroutine_fn (*init_in_iov_from_pdu)(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size); + void coroutine_fn (*init_out_iov_from_pdu)(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size); + void coroutine_fn (*push_and_notify)(V9fsPDU *pdu); + size_t coroutine_fn (*msize_limit)(V9fsState *s); + size_t coroutine_fn (*response_buffer_size)(V9fsPDU *pdu); }; #endif diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 576a794ea4..2774fc2290 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -28,7 +28,7 @@ #include "qemu/module.h" #include "system/qtest.h" -static void virtio_9p_push_and_notify(V9fsPDU *pdu) +static void coroutine_fn virtio_9p_push_and_notify(V9fsPDU *pdu) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -117,8 +117,8 @@ static void virtio_9p_reset(VirtIODevice *vdev) v9fs_reset(&v->state); } -static ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap) +static ssize_t coroutine_fn +virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, va_list ap) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -135,8 +135,8 @@ static ssize_t virtio_pdu_vmarshal(V9fsPDU *pdu, size_t offset, return ret; } -static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, - const char *fmt, va_list ap) +static ssize_t coroutine_fn +virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, va_list ap) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -152,8 +152,9 @@ static ssize_t virtio_pdu_vunmarshal(V9fsPDU *pdu, size_t offset, return ret; } -static void virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size) +static void coroutine_fn +virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, size_t size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -172,8 +173,9 @@ static void virtio_init_in_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, *pniov = elem->in_num; } -static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, - unsigned int *pniov, size_t size) +static void coroutine_fn +virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, + unsigned int *pniov, size_t size) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); @@ -192,13 +194,13 @@ static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov, *pniov = elem->out_num; } -static size_t virtio_9p_msize_limit(V9fsState *s) +static size_t coroutine_fn virtio_9p_msize_limit(V9fsState *s) { const size_t guestPageSize = 4096; return (VIRTQUEUE_MAX_SIZE - 2) * guestPageSize; } -static size_t virtio_9p_response_buffer_size(V9fsPDU *pdu) +static size_t coroutine_fn virtio_9p_response_buffer_size(V9fsPDU *pdu) { V9fsState *s = pdu->s; V9fsVirtioState *v = container_of(s, V9fsVirtioState, state); diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index d44fa8d61e..06709d5408 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -136,10 +136,10 @@ static void xen_9pfs_out_sg(Xen9pfsRing *ring, } } -static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, - size_t offset, - const char *fmt, - va_list ap) +static ssize_t coroutine_fn xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, + size_t offset, + const char *fmt, + va_list ap) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); struct iovec in_sg[2]; @@ -161,10 +161,10 @@ static ssize_t xen_9pfs_pdu_vmarshal(V9fsPDU *pdu, return ret; } -static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, - size_t offset, - const char *fmt, - va_list ap) +static ssize_t coroutine_fn xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, + size_t offset, + const char *fmt, + va_list ap) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); struct iovec out_sg[2]; @@ -185,10 +185,10 @@ static ssize_t xen_9pfs_pdu_vunmarshal(V9fsPDU *pdu, return ret; } -static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, - struct iovec **piov, - unsigned int *pniov, - size_t size) +static void coroutine_fn xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; @@ -202,10 +202,10 @@ static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, *pniov = num; } -static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, - struct iovec **piov, - unsigned int *pniov, - size_t size) +static void coroutine_fn xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, + struct iovec **piov, + unsigned int *pniov, + size_t size) { Xen9pfsDev *xen_9pfs = container_of(pdu->s, Xen9pfsDev, state); Xen9pfsRing *ring = &xen_9pfs->rings[pdu->tag % xen_9pfs->num_rings]; @@ -234,7 +234,7 @@ again: *pniov = num; } -static void xen_9pfs_push_and_notify(V9fsPDU *pdu) +static void coroutine_fn xen_9pfs_push_and_notify(V9fsPDU *pdu) { RING_IDX prod; Xen9pfsDev *priv = container_of(pdu->s, Xen9pfsDev, state); From 0e83d6894534cfe4cf071381ea8466cc689e044e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 20 Jul 2026 10:55:33 +0400 Subject: [PATCH 161/860] migration/rdma: annotate and simplify wait_comp_channel() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function calls yield_until_fd_readable() (coroutine_fn) when in coroutine context, and polls with qemu_poll_ns() otherwise. Replace the migration-state proxy check with qemu_in_coroutine(), which directly tests what matters. Fixes: 2da776db4846 ("rdma: core logic") Reviewed-by: Peter Xu Signed-off-by: Marc-André Lureau --- migration/rdma.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index 3e37a1d440..438419d189 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -1348,17 +1348,13 @@ static int qemu_rdma_poll(RDMAContext *rdma, struct ibv_cq *cq, /* Wait for activity on the completion channel. * Returns 0 on success, none-0 on error. */ -static int qemu_rdma_wait_comp_channel(RDMAContext *rdma, - struct ibv_comp_channel *comp_channel) +static int coroutine_mixed_fn +qemu_rdma_wait_comp_channel(RDMAContext *rdma, + struct ibv_comp_channel *comp_channel) { struct rdma_cm_event *cm_event; - /* - * Coroutine doesn't start until migration_fd_process_incoming() - * so don't yield unless we know we're running inside of a coroutine. - */ - if (rdma->migration_started_on_destination && - migration_incoming_get_current()->state == MIGRATION_STATUS_ACTIVE) { + if (qemu_in_coroutine()) { yield_until_fd_readable(comp_channel->fd); } else { /* This is the source side, we're in a separate thread From 21a77a215809beafad1c4bfd12e691ec058eb6d0 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:55 +0200 Subject: [PATCH 162/860] parallels: fix integer overflow in header size calculation parallels_open() caches bat_entry_off(s->bat_size) - a uint32_t - in a plain int before it feeds into s->header_size. Near the "Catalog too large" bound the value exceeds INT_MAX and overflows on assignment. Match the cached value's type to bat_entry_off()'s return type. Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/parallels.c b/block/parallels.c index 7a90fb5220..59f00c64a6 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -1240,7 +1240,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, { BDRVParallelsState *s = bs->opaque; ParallelsHeader ph; - int ret, size, i; + int ret, i; + uint32_t size; int64_t file_nb_sectors, sector; uint32_t data_start; bool need_check = false; From a6e0519ea8ed6fc84fab9bf9ca82c7a55780f880 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:31:42 -0400 Subject: [PATCH 163/860] virtio: use masked features with set_features_ex virtio_set_features_nocheck() calls set_features_ex with guest-supplied feature bits, without masking the value with host features (unlike set_features which gets the correct val & host_features). This does not matter if the driver matches spec, but drivers can be malicious or buggy and set bit outside the host mask. Devices don't expect this, so unsupported guest feature bits getting set can break the host. In virtio-net, this can enable RSC without vnet header support and cause out-of-bounds reads from short packets. Pass the masked features to set_features_ex, consistent with set_features. Fixes: CVE-2026-63321 Fixes: 64a6a336f4 ("virtio: add support for negotiating extended features") Cc: Jason Wang Cc: Yuri Benditovich Cc: Paolo Abeni Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3623 Reported-by: huntr bubble Signed-off-by: Michael S. Tsirkin Message-ID: --- hw/virtio/virtio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f4d86a3655..7c19080db5 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3320,7 +3320,7 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, const uint64_t *val) virtio_features_and(tmp, val, vdev->host_features_ex); if (k->set_features_ex) { - k->set_features_ex(vdev, val); + k->set_features_ex(vdev, tmp); } else if (k->set_features) { bad = bad || virtio_features_use_ex(tmp); k->set_features(vdev, tmp[0]); From 52c7bb369b23dfcafb6e6d90665c777797b55e88 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 15 Jul 2026 12:33:31 -0400 Subject: [PATCH 164/860] virtio-net: fix OOB read in RSC receive path The RSC receive path parses incoming frames at guest_hdr_len byte offsets, but the backend buffer contains only host_hdr_len bytes of vnet header. If the lengths differ, RSC would read at the wrong offset and cause an OOB read. This is no longer possible after the previous patch, but the assumption seem fragile. Along the defense in depth lines, let's validate. To ensure we are not breaking any valid setups by mistake, warn and fall back to the normal receive path when host_hdr_len != guest_hdr_len. Fixes: CVE-2026-63321 Fixes: 2974e916df ("virtio-net: support RSC v4/v6 tcp traffic for Windows HCK") Cc: Jason Wang Cc: Yuri Benditovich Cc: Wei Xu Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3623 Reported-by: huntr bubble Signed-off-by: Michael S. Tsirkin Message-ID: --- hw/net/virtio-net.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f0e3beb290..2761644dfd 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -2674,6 +2674,13 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, { VirtIONet *n = qemu_get_nic_opaque(nc); if ((n->rsc4_enabled || n->rsc6_enabled)) { + /* this never happens with existing backends, but just in case. */ + if (n->host_hdr_len != n->guest_hdr_len) { + warn_report_once("virtio-net: host_hdr_len %zu != guest_hdr_len %zu, " + "skipping RSC", + n->host_hdr_len, n->guest_hdr_len); + return virtio_net_do_receive(nc, buf, size); + } return virtio_net_rsc_receive(nc, buf, size); } else { return virtio_net_do_receive(nc, buf, size); From 772488562053c1299fc3667a49b3ff1858f83979 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:32:02 -0400 Subject: [PATCH 165/860] virtio-net: fix short frame OOB read in receive_filter() Within virtio-net, receive_filter() reads Ethernet header fields without any length checks. But virtio-net sets do_not_pad in NetClientState, so backends such as socket forward frames at the size supplied by the peer without padding to the Ethernet minimum. A short frame thus causes an out-of-bounds read. Add size checks in receive_filter() and drop the truncated frames. Fixes: CVE-2026-63320 Fixes: 3831ab2094 ("qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti (Alex Williamson)") Cc: Jason Wang Cc: Alex Williamson Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3626 Reported-by: huntr bubble Signed-off-by: Michael S. Tsirkin Message-ID: --- hw/net/virtio-net.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 2761644dfd..7e0c984ec6 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1744,10 +1744,21 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) if (n->promisc) return 1; + if (size < n->host_hdr_len + 14) { + /* Truncated ethernet packet */ + return 0; + } + ptr += n->host_hdr_len; if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { - int vid = lduw_be_p(ptr + 14) & 0xfff; + int vid; + + /* Truncated vlan packet */ + if (size < n->host_hdr_len + 16) { + return 0; + } + vid = lduw_be_p(ptr + 14) & 0xfff; if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) return 0; } From fbdfdbb9920cb69408d53cb86a8e079a2054581e Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:33:18 -0400 Subject: [PATCH 166/860] libvhost-user: protect against OOB writes in vu_set_inflight_fd vu_set_inflight_fd() trusts the num_queues value from the VHOST_USER_SET_INFLIGHT_FD message without checking it against dev->max_queues, so an oversized value causes out-of-bounds writes to dev->vq. Front end is generally trusted so not a security problem, but OOB isn't a nice way to handle frontend bugs. Let's harden this a bit: check num_queues and panic if it's invalid. Fixes: 5f9ff1eff3 ("libvhost-user: Support tracking inflight I/O in shared memory") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3740 Cc: Stefano Garzarella Reported-by: xlabai Signed-off-by: Michael S. Tsirkin Message-ID: <23b3f12388c1035f208550df9de9944c22d8d534.1784899127.git.mst@redhat.com> --- subprojects/libvhost-user/libvhost-user.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 2c35bddd6f..8208265588 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -1998,6 +1998,8 @@ vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) if (vmsg->size != sizeof(vmsg->payload.inflight)) { vu_panic(dev, "Invalid get_inflight_fd message:%d", vmsg->size); + vmsg_close_fds(vmsg); + vmsg->fd_num = 0; vmsg->payload.inflight.mmap_size = 0; return true; } @@ -2005,6 +2007,15 @@ vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) num_queues = vmsg->payload.inflight.num_queues; queue_size = vmsg->payload.inflight.queue_size; + if (num_queues > dev->max_queues) { + vu_panic(dev, "Invalid get_inflight_fd num_queues: %"PRId16, + num_queues); + vmsg_close_fds(vmsg); + vmsg->fd_num = 0; + vmsg->payload.inflight.mmap_size = 0; + return true; + } + DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues); DPRINT("set_inflight_fd queue_size: %"PRId16"\n", queue_size); @@ -2052,6 +2063,7 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) vmsg->size != sizeof(vmsg->payload.inflight)) { vu_panic(dev, "Invalid set_inflight_fd message size:%d fds:%d", vmsg->size, vmsg->fd_num); + vmsg_close_fds(vmsg); return false; } @@ -2061,6 +2073,13 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) num_queues = vmsg->payload.inflight.num_queues; queue_size = vmsg->payload.inflight.queue_size; + if (num_queues > dev->max_queues) { + vu_panic(dev, "Invalid set_inflight_fd num_queues: %"PRId16, + num_queues); + close(fd); + return false; + } + DPRINT("set_inflight_fd mmap_size: %"PRId64"\n", mmap_size); DPRINT("set_inflight_fd mmap_offset: %"PRId64"\n", mmap_offset); DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues); @@ -2071,6 +2090,7 @@ vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) if (rc == MAP_FAILED) { vu_panic(dev, "set_inflight_fd mmap error: %s", strerror(errno)); + close(fd); return false; } From 758ef96a2dba157e930af7709f327435a153f7a3 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:33:33 -0400 Subject: [PATCH 167/860] libvhost-user: protect against OOB vring queue access SET_VRING_NUM, SET_VRING_ADDR, SET_VRING_BASE, and GET_VRING_BASE handlers all use the queue index from the message to access dev->vq[] without checking that it is below dev->max_queues, so a malformed message causes an out-of-bounds heap access. Frontend is trusted so not a security problem, but an OOB access is not a nice way to handle errors. Check, and panic. Fixes: 7b2e5c65f4 ("contrib: add libvhost-user") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3741 Cc: Stefano Garzarella Reported-by: xlabai Signed-off-by: Michael S. Tsirkin Message-ID: --- subprojects/libvhost-user/libvhost-user.c | 28 ++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 8208265588..42a5e2f1f5 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -1200,6 +1200,12 @@ vu_set_vring_num_exec(VuDev *dev, VhostUserMsg *vmsg) DPRINT("State.index: %u\n", index); DPRINT("State.num: %u\n", num); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_num index: %u", index); + return false; + } + dev->vq[index].vring.num = num; return false; @@ -1210,7 +1216,7 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) { struct vhost_vring_addr addr = vmsg->payload.addr, *vra = &addr; unsigned int index = vra->index; - VuVirtq *vq = &dev->vq[index]; + VuVirtq *vq; DPRINT("vhost_vring_addr:\n"); DPRINT(" index: %d\n", vra->index); @@ -1220,6 +1226,12 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->avail_user_addr); DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", (uint64_t)vra->log_guest_addr); + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_addr index: %u", index); + return false; + } + + vq = &dev->vq[index]; vq->vra = *vra; vq->vring.flags = vra->flags; vq->vring.log_guest_addr = vra->log_guest_addr; @@ -1256,6 +1268,12 @@ vu_set_vring_base_exec(VuDev *dev, VhostUserMsg *vmsg) DPRINT("State.index: %u\n", index); DPRINT("State.num: %u\n", num); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_base index: %u", index); + return false; + } + dev->vq[index].shadow_avail_idx = dev->vq[index].last_avail_idx = num; return false; @@ -1267,6 +1285,14 @@ vu_get_vring_base_exec(VuDev *dev, VhostUserMsg *vmsg) unsigned int index = vmsg->payload.state.index; DPRINT("State.index: %u\n", index); + + if (index >= dev->max_queues) { + vu_panic(dev, "Invalid vring_base index: %u", index); + vmsg->payload.state.num = 0; + vmsg->size = sizeof(vmsg->payload.state); + return true; + } + vmsg->payload.state.num = dev->vq[index].last_avail_idx; vmsg->size = sizeof(vmsg->payload.state); From 6dbdc271f56b42e921a662b65a862f503eb4448e Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 15 Jul 2026 13:50:40 +0200 Subject: [PATCH 168/860] hw/virtio: reject zero-length packed indirect descriptor table The split-ring path already rejects a zero-length indirect descriptor table since commit 7423192912af ("virtio: add checks for the size of the indirect table"). The packed-ring path is missing the same check, allowing a guest to trigger an assertion in address_space_cache_init() with a packed indirect descriptor that has len=0. Add the same !desc.len check to the packed-ring indirect validation in both virtqueue_packed_get_avail_bytes() and virtqueue_packed_pop(). Fixes: 86044b24e865 ("virtio: basic packed virtqueue support") Cc: jasowangio@gmail.com Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3984 Reported-by: dong ling Signed-off-by: Laurent Vivier Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260715115040.2186274-1-lvivier@redhat.com> --- hw/virtio/virtio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7c19080db5..2d33622daf 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1475,7 +1475,7 @@ static void virtqueue_packed_get_avail_bytes(VirtQueue *vq, } if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingPackedDesc)) { + if (!desc.len || (desc.len % sizeof(VRingPackedDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto err; } @@ -1927,7 +1927,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) vring_packed_desc_read(vdev, &desc, desc_cache, i, true); id = desc.id; if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingPackedDesc)) { + if (!desc.len || (desc.len % sizeof(VRingPackedDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } From 27806d2ddba2da97fb4137cba7d4172356c42914 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:35:14 -0400 Subject: [PATCH 169/860] vhost: do not crash on ring map failure When vhost_commit() rebuilds the memory region table after a flatview change, it revalidates cached host virtual addresses for active vring parts. If a mapping is stale, QEMU abort(). This is not a security problem - only the priviledged guest can control make it invalid - but not nice e.g. for driver debugging. Let's call virtio_error() instead, marking the device as broken. Fixes: 0ca1fd2d68 ("vhost: Simplify ring verification checks") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3783 Cc: Stefano Garzarella Cc: Dr. David Alan Gilbert Reported-by: Feifan Qian Signed-off-by: Michael S. Tsirkin Message-ID: <71961a7dc157f552303aeea8c99a75c5e1ce904e.1784898432.git.mst@redhat.com> Reviewed-by: Dr. David Alan Gilbert --- hw/virtio/vhost.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index af41841b52..371dca17dd 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -755,8 +755,9 @@ static void vhost_commit(MemoryListener *listener) (void *)(uintptr_t)dev->mem->regions[i].userspace_addr, dev->mem->regions[i].guest_phys_addr, dev->mem->regions[i].memory_size)) { - error_report("Verify ring failure on region %d", i); - abort(); + virtio_error(dev->vdev, + "Verify ring failure on region %d", i); + goto out; } } From f404bf0e6504e0412a00ea64708f17d2a5e3f869 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:35:57 -0400 Subject: [PATCH 170/860] virtio-scsi: fix SCSIRequest leak on a bad request When virtio_scsi_handle_cmd_vq() cleans up prepared requests after a malformed element in the same batch, it drops only one reference even though virtio_scsi_handle_cmd_req_prepare() leaves each unsubmitted SCSIRequest with two references. This leaks the request and allows repeated bad batches to cause unbounded host memory growth. Add a second scsi_req_unref() and clear hba_private first. Fixes: CVE-2026-61476 Fixes: 661e32fb3c ("virtio-scsi: convert virtio_scsi_bad_req() to use virtio_error()") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3875 Cc: Paolo Bonzini Cc: Fam Zheng Cc: Greg Kurz Reported-by: Feifan Qian Signed-off-by: Michael S. Tsirkin Message-ID: <5092cd4716e08d29731bfe85eea82a732b837ff4.1784895264.git.mst@redhat.com> --- hw/scsi/scsi-bus.c | 7 +++++++ hw/scsi/virtio-scsi.c | 2 ++ include/hw/scsi/scsi.h | 1 + 3 files changed, 10 insertions(+) diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index dccb2f25b2..deb43d5560 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1513,6 +1513,13 @@ void scsi_req_unref(SCSIRequest *req) } } +void scsi_req_unref_detach_hba(SCSIRequest *req) +{ + /* Unref when the HBA frees hba_private separately (e.g. virtio_scsi_free_req) */ + req->hba_private = NULL; + scsi_req_unref(req); +} + /* Tell the device that we finished processing this chunk of I/O. It will start the next chunk or complete the command. */ void scsi_req_continue(SCSIRequest *req) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 6c73768011..bf64d1231a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -931,7 +931,9 @@ static void virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq) req = QTAILQ_FIRST(&reqs); QTAILQ_REMOVE(&reqs, req, next); defer_call_end(); + /* Drop both the ref from _prepare and the initial ref */ scsi_req_unref(req->sreq); + scsi_req_unref_detach_hba(req->sreq); virtqueue_detach_element(req->vq, &req->elem, 0); virtio_scsi_free_req(req); } diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 5f83e58d1d..c60c6e8810 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -221,6 +221,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun, int32_t scsi_req_enqueue(SCSIRequest *req); SCSIRequest *scsi_req_ref(SCSIRequest *req); void scsi_req_unref(SCSIRequest *req); +void scsi_req_unref_detach_hba(SCSIRequest *req); int scsi_bus_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, uint8_t *buf, size_t buf_len, void *hba_private); From a5cff318f06cd06b37224e15ee74d64d1df8b12b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 24 Jul 2026 16:09:31 -0400 Subject: [PATCH 171/860] virtio-mmio: fix QUEUE_NUM_MAX virtio-mmio reports VIRTQUEUE_MAX_SIZE (1024) as QUEUE_NUM_MAX for every queue, regardless of the size the device passes to virtio_add_queue(). This works by accident because QEMU mostly does not care about the ring size - the guest is the one allocating memory here. But this changes with in-order vqs where qemu is the one allocating resources. Now, specifying a larger vq than allocated causes an OOB memory access. To fix: - for new machine types, report the actual max queue size to guest - for old machine types, use a compat property to allocate 1k sized queues Fixes: 525d82e323 ("virtio: fix queue size validation against allocated maximum") Fixes: CVE-2026-50626 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3882 Cc: Peter Maydell Message-ID: <8715acbb9516e67e2a776cda6f9edf105343f788.1784930765.git.mst@redhat.com> Acked-by: Yonggang Luo Reported-by: Miku Hatsune Signed-off-by: Michael S. Tsirkin --- hw/core/machine.c | 1 + hw/virtio/virtio-mmio.c | 7 +++---- hw/virtio/virtio.c | 11 +++++++++++ include/hw/virtio/virtio-bus.h | 1 + include/hw/virtio/virtio-mmio.h | 1 + 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 805148678d..73b4d82b4a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -41,6 +41,7 @@ #include "hw/arm/smmuv3.h" GlobalProperty hw_compat_11_0[] = { + { "virtio-mmio", VIRTIO_QUEUE_SIZE_OVERRIDE, "1024" }, { "chardev-vc", "encoding", "cp437" }, { "tpm-crb", "cap-chunk", "off" }, { "tpm-crb", "x-allow-chunk-migration", "off" }, diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 58c6d46aab..55ceaeef5f 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -172,10 +172,7 @@ static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) >> (32 * proxy->host_features_sel); } case VIRTIO_MMIO_QUEUE_NUM_MAX: - if (!virtio_queue_get_num(vdev, vdev->queue_sel)) { - return 0; - } - return VIRTQUEUE_MAX_SIZE; + return virtio_queue_get_max_num(vdev, vdev->queue_sel); case VIRTIO_MMIO_QUEUE_PFN: if (!proxy->legacy) { qemu_log_mask(LOG_GUEST_ERROR, @@ -738,6 +735,8 @@ static const Property virtio_mmio_properties[] = { DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true), DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags, VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT16(VIRTIO_QUEUE_SIZE_OVERRIDE, VirtIOMMIOProxy, + override_queue_size, 0), }; static void virtio_mmio_realizefn(DeviceState *d, Error **errp) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2d33622daf..cdc8ba76f1 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2568,6 +2568,17 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, if (i == VIRTIO_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) abort(); + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + if (qbus && qbus->parent && + object_property_find(OBJECT(qbus->parent), VIRTIO_QUEUE_SIZE_OVERRIDE)) { + int override = object_property_get_int(OBJECT(qbus->parent), + VIRTIO_QUEUE_SIZE_OVERRIDE, + &error_abort); + if (override) { + queue_size = override; + } + } + vdev->vq[i].vring.num = queue_size; vdev->vq[i].vring.num_default = queue_size; vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 1a2d396156..f80fd71424 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -30,6 +30,7 @@ #include "qom/object.h" #define TYPE_VIRTIO_BUS "virtio-bus" +#define VIRTIO_QUEUE_SIZE_OVERRIDE "x-override-queue-size" typedef struct VirtioBusClass VirtioBusClass; typedef struct VirtioBusState VirtioBusState; DECLARE_OBJ_CHECKERS(VirtioBusState, VirtioBusClass, diff --git a/include/hw/virtio/virtio-mmio.h b/include/hw/virtio/virtio-mmio.h index 1644d09810..0a9069868c 100644 --- a/include/hw/virtio/virtio-mmio.h +++ b/include/hw/virtio/virtio-mmio.h @@ -69,6 +69,7 @@ struct VirtIOMMIOProxy { /* Fields only used for non-legacy (v2) devices */ uint32_t guest_features[2]; VirtIOMMIOQueue vqs[VIRTIO_QUEUE_MAX]; + uint16_t override_queue_size; }; #endif From d530f2dfbd2d973b17a6d0ffbfe2afb692bdd69c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 20 Jul 2026 01:26:42 -0400 Subject: [PATCH 172/860] virtio: fix queue size validation against allocated maximum virtio_add_queue() allocates used_elems for num_default entries, but virtio_queue_set_num() accepts larger guest-supplied queue sizes up to VIRTQUEUE_MAX_SIZE. With VIRTIO_F_IN_ORDER, this lets the guest drive used_elems accesses past the allocation and cause out-of-bounds reads and writes. Reject queue sizes larger than num_default in virtio_queue_set_num() and mark the device broken. Fixes: e63c0ba1bc ("virtio: Add support for guest setting of queue size") Fixes: CVE-2026-50626 Cc: Peter Maydell Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3882 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3921 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3923 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3613 Reported-by: huntr bubble Reported-by: Jia Jia Reported-by: Miku Hatsune Signed-off-by: Michael S. Tsirkin Message-ID: --- hw/virtio/virtio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index cdc8ba76f1..4a6430c31c 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2418,6 +2418,11 @@ void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) num < 0) { return; } + if (num > vdev->vq[n].vring.num_default) { + virtio_error(vdev, "virtio: queue %d size %d exceeds max size %u", + n, num, vdev->vq[n].vring.num_default); + return; + } vdev->vq[n].vring.num = num; } From d6384ad1c15962092c2281efedc118152de6a794 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 20 Jul 2026 01:26:56 -0400 Subject: [PATCH 173/860] virtio: stop migrating num_default, validate vring.num on load num_default tracks the allocation size of used_elems, set by virtio_add_queue(). Migrating it via the ringsize subsection is wrong: a migration stream (malicious or simply from a different configuration) can inflate num_default so that virtio_queue_set_num() accepts oversized values, leading to OOB access on the used_elems array. It is not even migrated consistently: a configuration with a smaller num_default could thinkably migrate and work but in the common case of num == num_default the value is not actually sent. Stop migrating num_default: make virtio_ringsize_needed() return false so the subsection is never sent, and use VMSTATE_UNUSED to consume the field from old streams without applying it. The destination keeps its local num_default from virtio_add_queue(), which matches the actual allocation. Also validate vring.num against num_default when loading the core virtio state, rejecting streams that supply a queue size larger than the locally allocated maximum. Fixes: 46c5d0823d ("virtio: ring sizes vs. reset") Fixes: 50e5ae4dc3 ("migration/virtio: Remove simple .get/.put use") Cc: Cornelia Huck Cc: Peter Maydell Signed-off-by: Michael S. Tsirkin Message-ID: <3e6a7c403f93acc37af6a6332fdc65049ae218fb.1784894327.git.mst@redhat.com> --- hw/virtio/virtio.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 4a6430c31c..2766217ddd 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2805,14 +2805,6 @@ static bool virtio_packed_virtqueue_needed(void *opaque) static bool virtio_ringsize_needed(void *opaque) { - VirtIODevice *vdev = opaque; - int i; - - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num != vdev->vq[i].vring.num_default) { - return true; - } - } return false; } @@ -2901,7 +2893,7 @@ static const VMStateDescription vmstate_ringsize = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_UINT32(vring.num_default, struct VirtQueue), + VMSTATE_UNUSED(sizeof(uint32_t)), VMSTATE_END_OF_LIST() } }; @@ -3581,6 +3573,12 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) for (i = 0; i < num; i++) { vdev->vq[i].vring.num = qemu_get_be32(f); + if (vdev->vq[i].vring.num > vdev->vq[i].vring.num_default) { + error_report("VQ %d vring.num %u exceeds allocated max %u", + i, vdev->vq[i].vring.num, + vdev->vq[i].vring.num_default); + return -1; + } if (k->has_variable_vring_alignment) { vdev->vq[i].vring.align = qemu_get_be32(f); } From 665c854390e0db15ed55b985ca850c36b28b0e57 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:38:25 -0400 Subject: [PATCH 174/860] virtio: fail early on bad config_len in migration virtio_load() attempts to load config_len bytes from the migration stream. If that's huge (e.g. 4g) this will uselessly spin beyond the end of the stream for seconds. Not nice. Check qemu_file_get_error() and bail out early, instead. Also note that config_len is int32_t but is coerced to unsigned when used. Switch it to uint32_t to make this clearer. Fixes: 2f5732e964 ("Allow mismatched virtio config-len") Cc: Dr. David Alan Gilbert Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3891 Reported-by: Feifan Qian Signed-off-by: Michael S. Tsirkin Message-ID: Reviewed-by: Dr. David Alan Gilbert --- hw/virtio/virtio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2766217ddd..979c4065b7 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3505,7 +3505,7 @@ int coroutine_mixed_fn virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) { int i, ret; - int32_t config_len; + uint32_t config_len; uint32_t num; uint32_t features; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -3553,6 +3553,9 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) qemu_get_buffer(f, vdev->config, MIN(config_len, vdev->config_len)); while (config_len > vdev->config_len) { + if (qemu_file_get_error(f)) { + return -1; + } qemu_get_byte(f); config_len--; } From cf89b769cdd1933796099706387d3b2a9a865cae Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:38:40 -0400 Subject: [PATCH 175/860] vhost-user: assert nregions within limit scrub_shadow_regions() and vhost_user_add_remove_regions() use fixed-size stack arrays sized to VHOST_USER_MAX_RAM_SLOTS and index them with dev->mem->nregions. nregions is calculated to never overrun these, but let's add an assert to make sure we don't get a stack overflow if there's a bug. Fixes: f1aeb14b08 ("Transmit vhost-user memory regions individually") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3910 Cc: Stefano Garzarella Cc: Raphael Norwitz Reported-by: Feifan Qian Signed-off-by: Michael S. Tsirkin Message-ID: <48fb8411f67e525872fb19618a886e52b670ab7f.1784896199.git.mst@redhat.com> --- hw/virtio/vhost-user.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 517cc4ca71..2881cec72d 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -946,6 +946,9 @@ static int vhost_user_add_remove_regions(struct vhost_dev *dev, msg->hdr.size = sizeof(msg->payload.mem_reg); + /* Ensure nregions fits the fixed-size arrays used below. */ + assert(dev->mem->nregions <= VHOST_USER_MAX_RAM_SLOTS); + /* Find the regions which need to be removed or added. */ scrub_shadow_regions(dev, add_reg, &nr_add_reg, rem_reg, &nr_rem_reg, shadow_pcb, track_ramblocks); From 5cc182ba39a3ca8ec9ba0576de9696be76dc087d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 8 Jul 2026 11:39:22 -0400 Subject: [PATCH 176/860] virtio-pmem: wait for flush requests on unrealize virtio_pmem_flush submits fsync requests to the thread pool and stores a VirtIOPMEM pointer in each request. If device is deleted e.g. by hot-unplug, once these complete, done_cb can run after virtio_pmem_unrealize frees the device, causing a use-after-free. Track in-flight requests and wait in virtio_pmem_unrealize until their completions finish before tearing the device down. Fixes: CVE-2026-63323 Fixes: 5f503cd9f3 ("virtio-pmem: add virtio device") Cc: David Hildenbrand Cc: Pankaj Gupta Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3938 Reported-by: Jia Jia Signed-off-by: Michael S. Tsirkin Message-ID: <417b6685f37ce818c660ca3c84945992f5c30dcf.1784894206.git.mst@redhat.com> --- hw/virtio/virtio-pmem.c | 17 +++++++++++++++-- include/hw/virtio/virtio-pmem.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index c3b3299c9c..6f7271c140 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -23,6 +23,7 @@ #include "standard-headers/linux/virtio_pmem.h" #include "system/hostmem.h" #include "block/thread-pool.h" +#include "qemu/aio-wait.h" #include "trace.h" typedef struct VirtIODeviceRequest { @@ -54,14 +55,20 @@ static int worker_cb(void *opaque) static void done_cb(void *opaque, int ret) { VirtIODeviceRequest *req_data = opaque; + VirtIOPMEM *pmem = req_data->pmem; int len = iov_from_buf(req_data->elem.in_sg, req_data->elem.in_num, 0, &req_data->resp, sizeof(struct virtio_pmem_resp)); /* Callbacks are serialized, so no need to use atomic ops. */ - virtqueue_push(req_data->pmem->rq_vq, &req_data->elem, len); - virtio_notify((VirtIODevice *)req_data->pmem, req_data->pmem->rq_vq); + virtqueue_push(pmem->rq_vq, &req_data->elem, len); + virtio_notify((VirtIODevice *)pmem, pmem->rq_vq); trace_virtio_pmem_response(); g_free(req_data); + + pmem->inflight--; + if (!pmem->inflight) { + aio_wait_kick(); + } } static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) @@ -85,6 +92,7 @@ static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) req_data->fd = memory_region_get_fd(&backend->mr); req_data->pmem = pmem; req_data->vdev = vdev; + pmem->inflight++; thread_pool_submit_aio(worker_cb, req_data, done_cb, req_data); } @@ -122,6 +130,7 @@ static void virtio_pmem_realize(DeviceState *dev, Error **errp) host_memory_backend_set_mapped(pmem->memdev, true); virtio_init(vdev, VIRTIO_ID_PMEM, sizeof(struct virtio_pmem_config)); pmem->rq_vq = virtio_add_queue(vdev, 128, virtio_pmem_flush); + pmem->inflight = 1; } static void virtio_pmem_unrealize(DeviceState *dev) @@ -129,6 +138,10 @@ static void virtio_pmem_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOPMEM *pmem = VIRTIO_PMEM(dev); + /* Release the device's own reference and wait for in-flight flushes */ + pmem->inflight--; + AIO_WAIT_WHILE(NULL, pmem->inflight > 0); + host_memory_backend_set_mapped(pmem->memdev, false); virtio_delete_queue(pmem->rq_vq); virtio_cleanup(vdev); diff --git a/include/hw/virtio/virtio-pmem.h b/include/hw/virtio/virtio-pmem.h index 9cce600d0b..bb959d10cf 100644 --- a/include/hw/virtio/virtio-pmem.h +++ b/include/hw/virtio/virtio-pmem.h @@ -32,6 +32,7 @@ struct VirtIOPMEM { VirtQueue *rq_vq; uint64_t start; HostMemoryBackend *memdev; + unsigned int inflight; }; struct VirtIOPMEMClass { From bf21298c082cc5561072bbfd79216d478861a5cd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 24 Jul 2026 07:26:20 -0400 Subject: [PATCH 177/860] libvhost-user: validate last_batch_head in vu_check_queue_inflights vu_check_queue_inflights uses last_batch_head from the frontend-controlled inflight shared memory as an index into desc[] without bounds checking. A malicious or buggy frontend can set last_batch_head >= desc_num, causing an out-of-bounds write. Validate last_batch_head before using it. Note: the value is not guest-accessible so not a security vulnerability. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3974 Fixes: 5f9ff1eff3 ("libvhost-user: Support tracking inflight I/O in shared memory") Cc: Xie Yongji Cc: Stefano Garzarella Reported-by: BB CC Signed-off-by: Michael S. Tsirkin Message-Id: <8133770c75c9907578dd551f4d468140a0a75cd2.1784892981.git.mst@redhat.com> --- subprojects/libvhost-user/libvhost-user.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index 42a5e2f1f5..c1c13dbc90 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -1379,6 +1379,12 @@ vu_check_queue_inflights(VuDev *dev, VuVirtq *vq) vq->counter = 0; if (unlikely(vq->inflight->used_idx != vq->used_idx)) { + if (vq->inflight->last_batch_head >= vq->inflight->desc_num) { + vu_panic(dev, "vu_check_queue_inflights: last_batch_head %u " + "out of range (desc_num %u)", + vq->inflight->last_batch_head, vq->inflight->desc_num); + return -1; + } vq->inflight->desc[vq->inflight->last_batch_head].inflight = 0; barrier(); From b9d248dfaca5e31377ea4f7204aae788a050bafd Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Fri, 24 Jul 2026 07:26:29 -0400 Subject: [PATCH 178/860] libvhost-user: fix heap overflow in vu_check_queue_inflights vu_check_queue_inflights counts inflight descriptors using inflight == 1 but copies entries using inflight != 0. If the inflight field contains an unexpected non-0/1 value, the function copies more entries than it allocates and overflows the heap buffer. Stop the copy pass once resubmit_num reaches the counted inuse value. Note: the value is not guest-accessible so not a security vulnerability. Fixes: CVE-2026-63110 Fixes: 5f9ff1eff3 ("libvhost-user: Support tracking inflight I/O in shared memory") Cc: Xie Yongji Cc: Stefano Garzarella Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3974 Reported-by: BB CC Signed-off-by: Michael S. Tsirkin Message-Id: --- subprojects/libvhost-user/libvhost-user.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/subprojects/libvhost-user/libvhost-user.c b/subprojects/libvhost-user/libvhost-user.c index c1c13dbc90..a74d814bb4 100644 --- a/subprojects/libvhost-user/libvhost-user.c +++ b/subprojects/libvhost-user/libvhost-user.c @@ -1408,6 +1408,13 @@ vu_check_queue_inflights(VuDev *dev, VuVirtq *vq) for (i = 0; i < vq->inflight->desc_num; i++) { if (vq->inflight->desc[i].inflight) { + /* + * We earlier counted exactly vq->inuse in flight - + * what is going on? + */ + if (vq->resubmit_num >= vq->inuse) { + return -1; + } vq->resubmit_list[vq->resubmit_num].index = i; vq->resubmit_list[vq->resubmit_num].counter = vq->inflight->desc[i].counter; From 733a98a552e4bd68932de1f58bd6ea39b57b261c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 24 Jun 2026 10:43:46 -0400 Subject: [PATCH 179/860] libvduse: validate vq size libvduse assumes that vq size (aka vq num) is below VIRTQUEUE_MAX_SIZE and maps logs large enough based on this assumption. However, vduse_queue_enable() accepts the vq size returned through VDUSE_VQ_GET_INFO without validation, so a value above VIRTQUEUE_MAX_SIZE (1024) overruns the inflight log and causes out-of-bounds writes in vduse_queue_inflight_get(). According to the virtio spec, vq size can only be reduced, not increased, so vq size must not exceed the previously configured max_size, but the kernel vduse module does not validate this for us, and we should not trust another process to follow the spec. Validate and reject vq size values above VIRTQUEUE_MAX_SIZE. Fixes: CVE-2026-61402 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3652 Reported-by: Jia Jia Message-ID: Signed-off-by: Michael S. Tsirkin --- subprojects/libvduse/libvduse.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/subprojects/libvduse/libvduse.c b/subprojects/libvduse/libvduse.c index df9ca5e56f..712b97b4c3 100644 --- a/subprojects/libvduse/libvduse.c +++ b/subprojects/libvduse/libvduse.c @@ -902,6 +902,11 @@ static void vduse_queue_enable(VduseVirtq *vq) return; } + if (vq_info.num > VIRTQUEUE_MAX_SIZE) { + fprintf(stderr, "vq[%d] vring num %u exceeds max %u\n", + vq->index, vq_info.num, VIRTQUEUE_MAX_SIZE); + return; + } vq->vring.num = vq_info.num; vq->vring.desc_addr = vq_info.desc_addr; vq->vring.avail_addr = vq_info.driver_addr; From 67550d65b4bfdbcbfee2dae7ae1ebbbccd01dfe5 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 23 Jul 2026 07:58:43 -0400 Subject: [PATCH 180/860] virtio-iommu: fix OOM due to unbounded call_rcu Currently, within virtio-iommu, handle_command processes the command vq without any limits on the number of entries processed. This can easily and repeatedly enable/disable multiple memory regions. Within the memory code, this causes an accumulation of an unbounded number of RCU-deferred FlatViews - each of these is supposed to be freed with call_rcu, but that never happens because the main thread never returns to the main loop. Given FlatView is big, it's easy to have this balloon out to multiple Gigabytes of memory. Limit the loop defer any remaining work to a timer. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3930 Cc: Eric Auger Cc: Jean-Philippe Brucker Reviewed-by: Eric Auger Tested-by: Eric Auger Reported-by: Jia Jia Signed-off-by: Michael S. Tsirkin Message-ID: --- hw/virtio/virtio-iommu.c | 29 +++++++++++++++++++++++++++++ include/hw/virtio/virtio-iommu.h | 1 + 2 files changed, 30 insertions(+) diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 08f7e8b783..533bd5073f 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -993,6 +993,18 @@ static int virtio_iommu_handle_probe(VirtIOIOMMU *s, return ret ? ret : virtio_iommu_probe(s, &req, buf); } +static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq); + +static void virtio_iommu_handle_command_timer(void *opaque) +{ + VirtIOIOMMU *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + if (virtio_device_started(vdev, vdev->status) && !vdev->broken) { + virtio_iommu_handle_command(vdev, s->req_vq); + } +} + static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) { VirtIOIOMMU *s = VIRTIO_IOMMU(vdev); @@ -1003,10 +1015,17 @@ static void virtio_iommu_handle_command(VirtIODevice *vdev, VirtQueue *vq) struct iovec *iov; void *buf = NULL; size_t sz; + unsigned int batch = 0; for (;;) { size_t output_size = sizeof(tail); + if (++batch > virtio_queue_get_num(vdev, virtio_get_queue_index(vq))) { + timer_mod(s->cmd_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 1); + break; + } + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); if (!elem) { return; @@ -1416,6 +1435,8 @@ static void virtio_iommu_device_realize(DeviceState *dev, Error **errp) s->req_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, virtio_iommu_handle_command); s->event_vq = virtio_add_queue(vdev, VIOMMU_DEFAULT_QUEUE_SIZE, NULL); + s->cmd_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, + virtio_iommu_handle_command_timer, s); /* * config.bypass is needed to get initial address space early, such as @@ -1498,6 +1519,7 @@ static void virtio_iommu_device_unrealize(DeviceState *dev) qemu_rec_mutex_destroy(&s->mutex); + timer_free(s->cmd_timer); virtio_delete_queue(s->req_vq); virtio_delete_queue(s->event_vq); virtio_cleanup(vdev); @@ -1509,6 +1531,8 @@ static void virtio_iommu_device_reset_exit(Object *obj, ResetType type) trace_virtio_iommu_device_reset_exit(); + timer_del(s->cmd_timer); + if (s->domains) { g_tree_destroy(s->domains); } @@ -1628,6 +1652,11 @@ static int iommu_post_load(void *opaque, int version_id) * still correct. */ virtio_iommu_switch_address_space_all(s); + + if (virtio_device_started(VIRTIO_DEVICE(s), VIRTIO_DEVICE(s)->status)) { + timer_mod(s->cmd_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) + 1); + } return 0; } diff --git a/include/hw/virtio/virtio-iommu.h b/include/hw/virtio/virtio-iommu.h index 3b86050f2c..1f265540ad 100644 --- a/include/hw/virtio/virtio-iommu.h +++ b/include/hw/virtio/virtio-iommu.h @@ -65,6 +65,7 @@ struct VirtIOIOMMU { GTree *domains; QemuRecMutex mutex; GTree *endpoints; + QEMUTimer *cmd_timer; bool boot_bypass; Notifier machine_done; bool granule_frozen; From bdac94e9cfdff4d9d3c1a6f7cd995e4e4f04d7a9 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 20 Apr 2026 08:07:05 +0300 Subject: [PATCH 181/860] virtio-snd: check rx buffer descriptor size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It must be at least sizeof(virtio_snd_pcm_status). I haven't verified if it's possible to get an underflow, but coverity points it out in CID 1547527 so add a check. Reviewed-by: Alex Bennée Signed-off-by: Manos Pitsidianakis Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260420-virtio-fixups-v3-1-07aef1eff9d2@linaro.org> --- hw/audio/virtio-snd.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index fb5cff3866..93fbcfb43f 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -970,12 +970,14 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) } stream = vsnd->pcm.streams[stream_id]; - if (stream == NULL || stream->info.direction != VIRTIO_SND_D_INPUT) { + size = iov_size(elem->in_sg, elem->in_num); + if (stream == NULL + || stream->info.direction != VIRTIO_SND_D_INPUT + || size < sizeof(virtio_snd_pcm_status)) { goto rx_err; } + size -= sizeof(virtio_snd_pcm_status); WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { - size = iov_size(elem->in_sg, elem->in_num) - - sizeof(virtio_snd_pcm_status); buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); buffer->elem = elem; buffer->vq = vq; From fa9c0c9f716e39309ffdd95a5b7afc33cc0692d8 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Mon, 20 Apr 2026 08:07:06 +0300 Subject: [PATCH 182/860] virtio-snd: check for overflow before g_malloc0 Coverity points out one g_malloc0 overflow, but it seems to be a false positive. Add a check to it regardless to fortify the code, and also add checks for every other g_malloc0 use. Resolves: Coverity CID 1547527 Signed-off-by: Manos Pitsidianakis Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260420-virtio-fixups-v3-2-07aef1eff9d2@linaro.org> --- hw/audio/virtio-snd.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 93fbcfb43f..694bcebb60 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -850,7 +850,7 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) VirtIOSound *vsnd = VIRTIO_SND(vdev); VirtIOSoundPCMBuffer *buffer; VirtQueueElement *elem; - size_t msg_sz, size; + size_t msg_sz, size, tmp; virtio_snd_pcm_xfer hdr; uint32_t stream_id; /* @@ -880,6 +880,8 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) if (msg_sz != sizeof(virtio_snd_pcm_xfer)) { goto tx_err; } + assert(iov_size(elem->out_sg, elem->out_num) >= msg_sz); + size = iov_size(elem->out_sg, elem->out_num) - msg_sz; stream_id = le32_to_cpu(hdr.stream_id); if (stream_id >= vsnd->snd_conf.streams @@ -892,9 +894,11 @@ static void virtio_snd_handle_tx_xfer(VirtIODevice *vdev, VirtQueue *vq) goto tx_err; } + /* Check for g_malloc0 overflow. */ + if (!g_size_checked_add(&tmp, sizeof(VirtIOSoundPCMBuffer), size)) { + goto tx_err; + } WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { - size = iov_size(elem->out_sg, elem->out_num) - msg_sz; - buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); buffer->elem = elem; buffer->populated = false; @@ -932,7 +936,7 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) VirtIOSound *vsnd = VIRTIO_SND(vdev); VirtIOSoundPCMBuffer *buffer; VirtQueueElement *elem; - size_t msg_sz, size; + size_t msg_sz, size, tmp; virtio_snd_pcm_xfer hdr; uint32_t stream_id; /* @@ -977,6 +981,10 @@ static void virtio_snd_handle_rx_xfer(VirtIODevice *vdev, VirtQueue *vq) goto rx_err; } size -= sizeof(virtio_snd_pcm_status); + /* Check for g_malloc0 overflow. */ + if (!g_size_checked_add(&tmp, sizeof(VirtIOSoundPCMBuffer), size)) { + goto rx_err; + } WITH_QEMU_LOCK_GUARD(&stream->queue_mutex) { buffer = g_malloc0(sizeof(VirtIOSoundPCMBuffer) + size); buffer->elem = elem; From 7ea56dcc9406b1588699e510439f782da7067d3e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 8 Jul 2026 13:10:09 +0100 Subject: [PATCH 183/860] hw/pci-host/q35.c: Always initialize smram-region even if SMM disabled The MCHPCIState::smram_region looks like it ought to be SMM-specific, but it isn't, because its behaviour is "alias the PCI address space into system memory at the SMRAM_C_BASE offset", and it must be enabled for "hide SMRAM", and disabled for "show SMRAM". If the SMRAM regions are disabled, we want "hide SMRAM", so we need to initialize and place this MR. Do this in the minimal way, by moving the "bail out of realize if has_smm_ranges is false" check down below the initialization code. This fixes a bug where disabling SMM causes the VGA screen to be blank during seabios output, until the OS graphics driver is initialized. This is most obvious for accelerators which have no SMM support (e.g. NVMM, HVF, WHPX) as there smm=off is the default, but you can also see it on KVM and TCG if you explicitly pass smm=off: qemu-system-x86_64 -machine q35,accel=kvm,smm=off The early return is bug-prone, so we can refactor the code to clean it up, but this is the minimal bug fix for backports, and is what Debian used to work around this: https://salsa.debian.org/qemu-team/qemu/-/commit/6e0766f0f897dc2b75ab87dd59da0d4639bb37ee Another proposed fix for this: https://patchew.org/QEMU/20260413170407.57574-1-mohamed@unpredictable.fr/ also moves an early return in mch_update-smram() and tweaks mch_update_smram() accordingly. This shouldn't be necessary, because in the no-SMM case smram_region should always be enabled and we don't want to allow the guest to make it disabled. NetBSD bug: https://gnats.NetBSD.org/59721 Cc: qemu-stable@nongnu.org Fixes: b07bf7b7 ("q35: Introduce smm_ranges property for q35-pci-host") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2608 Reported-by: Kroese (gitlab @kroese) Signed-off-by: Peter Maydell Signed-off-by: Michael Tokarev Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260708121011.1653365-2-peter.maydell@linaro.org> --- hw/pci-host/q35.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 4784b8f59b..47e726c41d 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -596,11 +596,14 @@ static void mch_realize(PCIDevice *d, Error **errp) PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); } - if (!mch->has_smm_ranges) { - return; - } - - /* if *disabled* show SMRAM to all CPUs */ + /* + * This memory region looks like it's SMM specific, but it is not. + * It's an alias that makes the pci_address_space appear in system + * memory at the SMRAM_C_BASE address. The alias is enabled when the + * CPU should not see SMRAM, and *disabled* when the low SMRAM should be + * visible. So for non-SMM configs we need to create the alias, and + * leave it permanently enabled. + */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, MCH_HOST_BRIDGE_SMRAM_C_SIZE); @@ -608,6 +611,10 @@ static void mch_realize(PCIDevice *d, Error **errp) &mch->smram_region, 1); memory_region_set_enabled(&mch->smram_region, true); + if (!mch->has_smm_ranges) { + return; + } + memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, MCH_HOST_BRIDGE_SMRAM_C_SIZE); From a90fcf0080da2bd372f0a79dc243d8d2ede1b244 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 8 Jul 2026 13:10:10 +0100 Subject: [PATCH 184/860] hw/pci-host/q35.c: Factor out creation of SMRAM MRs mch_realize has a large section that deals with initializing the SMRAM-specific MemoryRegions. Currently we do an early return from the realize function if mch->has_smm_ranges is false, but this has the potential for bugs if somebody adds new code at the end of the function that isn't SMM-specific. Pull the MR init code out into its own function, so we can do the smm-ranges specific handling in the realize function in a more obvious way. This commit shouldn't change behaviour at all. Signed-off-by: Peter Maydell Reviewed-by: Michael Tokarev Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260708121011.1653365-3-peter.maydell@linaro.org> --- hw/pci-host/q35.c | 91 ++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 47e726c41d..e42b84b978 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -572,49 +572,9 @@ static void mch_reset(DeviceState *qdev) mch_update(mch); } -static void mch_realize(PCIDevice *d, Error **errp) +static void mch_init_smram_regions(MCHPCIState *mch) { - int i; - MCHPCIState *mch = MCH_PCI_DEVICE(d); - - if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) { - error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16, - mch->ext_tseg_mbytes); - return; - } - - /* setup pci memory mapping */ - pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); - - /* PAM */ - init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { - init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, - mch->system_memory, mch->pci_address_space, - PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); - } - - /* - * This memory region looks like it's SMM specific, but it is not. - * It's an alias that makes the pci_address_space appear in system - * memory at the SMRAM_C_BASE address. The alias is enabled when the - * CPU should not see SMRAM, and *disabled* when the low SMRAM should be - * visible. So for non-SMM configs we need to create the alias, and - * leave it permanently enabled. - */ - memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, - MCH_HOST_BRIDGE_SMRAM_C_SIZE); - memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, - &mch->smram_region, 1); - memory_region_set_enabled(&mch->smram_region, true); - - if (!mch->has_smm_ranges) { - return; - } - + /* Initialize all the SMRAM specific MemoryRegions */ memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, MCH_HOST_BRIDGE_SMRAM_C_SIZE); @@ -670,9 +630,52 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_set_enabled(&mch->smbase_window, false); memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMBASE_ADDR, &mch->smbase_window); +} - object_property_add_const_link(qdev_get_machine(), "smram", - OBJECT(&mch->smram)); +static void mch_realize(PCIDevice *d, Error **errp) +{ + int i; + MCHPCIState *mch = MCH_PCI_DEVICE(d); + + if (mch->ext_tseg_mbytes > MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX) { + error_setg(errp, "invalid extended-tseg-mbytes value: %" PRIu16, + mch->ext_tseg_mbytes); + return; + } + + /* setup pci memory mapping */ + pc_pci_as_mapping_init(mch->system_memory, mch->pci_address_space); + + /* PAM */ + init_pam(&mch->pam_regions[0], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_BIOS_BASE, PAM_BIOS_SIZE); + for (i = 0; i < ARRAY_SIZE(mch->pam_regions) - 1; ++i) { + init_pam(&mch->pam_regions[i + 1], OBJECT(mch), mch->ram_memory, + mch->system_memory, mch->pci_address_space, + PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); + } + + /* + * This memory region looks like it's SMM specific, but it is not. + * It's an alias that makes the pci_address_space appear in system + * memory at the SMRAM_C_BASE address. The alias is enabled when the + * CPU should not see SMRAM, and *disabled* when the low SMRAM should be + * visible. So for non-SMM configs we need to create the alias, and + * leave it permanently enabled. + */ + memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", + mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); + memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + &mch->smram_region, 1); + memory_region_set_enabled(&mch->smram_region, true); + + if (mch->has_smm_ranges) { + mch_init_smram_regions(mch); + object_property_add_const_link(qdev_get_machine(), "smram", + OBJECT(&mch->smram)); + } } static const Property mch_props[] = { From e07c67584c69da44a4d52df747a6fff1f411500d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 8 Jul 2026 13:10:11 +0100 Subject: [PATCH 185/860] hw/pci-host/q35.c: Avoid early return in mch_write_config() In mch_write_config() we return early if has_smm_ranges is false. This is slightly bug-prone because it leaves the door open to somebody later adding non-SMM-specific code at the bottom of the function. This case isn't as bad as the one in realize, because the function is a lot shorter. But putting the handling of the three SMM specific ranges into an if() rather than having an early return seems better. Signed-off-by: Peter Maydell Reviewed-by: Michael Tokarev Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260708121011.1653365-4-peter.maydell@linaro.org> --- hw/pci-host/q35.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index e42b84b978..f4556ad03a 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -485,22 +485,20 @@ static void mch_write_config(PCIDevice *d, mch_update_pciexbar(mch); } - if (!mch->has_smm_ranges) { - return; - } + if (mch->has_smm_ranges) { + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, + MCH_HOST_BRIDGE_SMRAM_SIZE)) { + mch_update_smram(mch); + } - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_SMRAM, - MCH_HOST_BRIDGE_SMRAM_SIZE)) { - mch_update_smram(mch); - } + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, + MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { + mch_update_ext_tseg_mbytes(mch); + } - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES, - MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { - mch_update_ext_tseg_mbytes(mch); - } - - if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { - mch_update_smbase_smram(mch); + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { + mch_update_smbase_smram(mch); + } } } From 421f45d03f328267ebf77fab823a7a25ab3d6c83 Mon Sep 17 00:00:00 2001 From: GuoHan Zhao Date: Thu, 9 Jul 2026 15:32:25 +0800 Subject: [PATCH 186/860] hw/virtio/vdpa-dev: pass set_config buffer to vhost backend vhost_vdpa_device_set_config() receives the updated config buffer, but forwards s->config to the vhost backend. Since s->config is refreshed by get_config(), it may contain stale backend state. Pass the supplied config buffer to vhost_dev_set_config() instead. Fixes: b430a2bd2303 ("vdpa: add vdpa-dev support") Signed-off-by: GuoHan Zhao Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260709073225.2341642-1-zhaoguohan@kylinos.cn> --- hw/virtio/vdpa-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index 089a77f4d0..6dc684ab09 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -212,7 +212,7 @@ vhost_vdpa_device_set_config(VirtIODevice *vdev, const uint8_t *config) VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); int ret; - ret = vhost_dev_set_config(&s->dev, s->config, 0, s->config_size, + ret = vhost_dev_set_config(&s->dev, config, 0, s->config_size, VHOST_SET_CONFIG_TYPE_FRONTEND); if (ret) { error_report("set device config space failed"); From 292902ae4e93dcf33414ec3c6951e02d497cc850 Mon Sep 17 00:00:00 2001 From: Haotian Jiang Date: Mon, 13 Jul 2026 15:23:36 +0800 Subject: [PATCH 187/860] hw/cxl: fix OOB access in cxl_doe_cdat_rsp via entry_handle cxl_doe_cdat_rsp() takes ent = req->entry_handle (uint16_t, fully guest-controlled, 0..0xFFFF) and directly indexes cdat->entry[ent] without checking ent < cdat->entry_len. For a default cxl-type3 with one volatile memory region, entry_len = 1 + CT3_CDAT_NUM_ENTRIES = 7, so any entry_handle >= 7 reads past the CDATEntry array into host heap. The OOB-read base/length are then used in memcpy(read_mbox + offset, base, len) at cxl_type3.c:298-299, leaking host heap memory to the guest via PCI_EXP_DOE_RD_DATA_MBOX, and potentially overflowing the 1 MiB read_mbox heap buffer when the OOB length field is large. The same bug exists in the cxl-upstream implementation. Existing checks do not bound ent: assert(cdat->entry_len) only ensures the table is loaded; the minimum-length check only guards against a truncated CDATReq; the entry_handle ternary at line 293 only decides the next-handle echo, not the current access; pcie_doe_get_obj_len reads header.length, not entry_handle. Reproduce: build QEMU with CONFIG_CXL, boot -M q35,cxl=on -device pxb-cxl,bus_nr=52 ... -device cxl-type3,... then send a CDATReq with entry_handle=0xFFFF via the DOE mailbox at config offset 0x190. Under ASAN this reports SEGV in cxl_doe_cdat_rsp at cxl_type3.c:281. Fixes: f5ee7413d5 ("hw/mem/cxl-type3: Add CXL CDAT Data Object Exchange") Fixes: 882877fc35 ("hw/pci-bridge/cxl-upstream: Add a CDAT table access DOE") Signed-off-by: Haotian Jiang Cc: qemu-stable@nongnu.org Reviewed-by: Jonathan Cameron Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260713072336.623604-2-jianghaotian.sunday@gmail.com> --- hw/mem/cxl_type3.c | 3 +++ hw/pci-bridge/cxl_upstream.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index cba05ec57d..28f41fa623 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -278,6 +278,9 @@ static bool cxl_doe_cdat_rsp(DOECap *doe_cap) } ent = req->entry_handle; + if (ent >= cdat->entry_len) { + return false; + } base = cdat->entry[ent].base; len = cdat->entry[ent].length; diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index b6281cbd4c..69773ff163 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -158,6 +158,9 @@ static bool cxl_doe_cdat_rsp(DOECap *doe_cap) } ent = req->entry_handle; + if (ent >= cdat->entry_len) { + return false; + } base = cdat->entry[ent].base; len = cdat->entry[ent].length; From 0691bb257a7cbfd62127208b49988705bfa2dfe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20MATHIEU--DRIF?= Date: Fri, 24 Jul 2026 11:15:32 +0000 Subject: [PATCH 188/860] intel_iommu: Check address mask before using it in pasid-based iotlb invalidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent a buggy driver to execute malformed invalidation operations. Add the same assert as in vtd_iotlb_page_invalidate. Link: https://gitlab.com/qemu-project/qemu/-/work_items/3619 Fixes: 6ebe6cf2a066 ("intel_iommu: Process PASID-based iotlb invalidation") Reported-by: huntr bubble Signed-off-by: Clement Mathieu--Drif Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260724111424.376680-1-clement.mathieu--drif@bull.com> --- hw/i386/intel_iommu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index d1af7a3135..82c3c3b2c3 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3021,6 +3021,8 @@ static void vtd_piotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, { VTDIOTLBPageInvInfo info; + assert(am <= VTD_MAMV); + info.domain_id = domain_id; info.pasid = pasid; info.addr = addr; @@ -3060,6 +3062,13 @@ static bool vtd_process_piotlb_desc(IntelIOMMUState *s, case VTD_INV_DESC_PIOTLB_PSI_IN_PASID: am = VTD_INV_DESC_PIOTLB_AM(inv_desc->val[1]); + if (am > VTD_MAMV) { + error_report_once("%s: invalid piotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (am=%u > VTD_MAMV=%llu)", + __func__, inv_desc->val[1], inv_desc->val[0], + am, VTD_MAMV); + return false; + } addr = (hwaddr) VTD_INV_DESC_PIOTLB_ADDR(inv_desc->val[1]); vtd_piotlb_page_invalidate(s, domain_id, pasid, addr, am, VTD_INV_DESC_PIOTLB_IH(inv_desc)); From df12999cc81339ffb252875608919c72ccc37bcd Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 24 Jul 2026 01:35:55 +0200 Subject: [PATCH 189/860] hw/net/virtio-net: Protect from DMA re-entrancy bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace qemu_bh_new_guarded() by virtio_bh_new_guarded() so the bus and device use the same guard. Otherwise the DMA-reentrancy protection can be bypassed. This update was missing in CVE-2024-3446 fix. Fixes: CVE-2026-66022 Cc: qemu-stable@nongnu.org Cc: Philippe Mathieu-Daudé Cc: alxndr@bu.edu Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4073 Reported-by: Giovanni Vignone Signed-off-by: Laurent Vivier Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260723233555.2970619-1-lvivier@redhat.com> --- hw/net/virtio-net.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 7e0c984ec6..814b99a43d 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3009,8 +3009,9 @@ static void virtio_net_add_queue(VirtIONet *n, int index) n->vqs[index].tx_vq = virtio_add_queue(vdev, n->net_conf.tx_queue_size, virtio_net_handle_tx_bh); - n->vqs[index].tx_bh = qemu_bh_new_guarded(virtio_net_tx_bh, &n->vqs[index], - &DEVICE(vdev)->mem_reentrancy_guard); + n->vqs[index].tx_bh = virtio_bh_new_guarded(DEVICE(vdev), + virtio_net_tx_bh, + &n->vqs[index]); } n->vqs[index].tx_waiting = 0; From 0be94d8d9c28e6b7235b34133d057090fe93be6c Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 24 Jul 2026 11:49:31 +0200 Subject: [PATCH 190/860] hw/virtio-rng: Fix host use-after-free (CVE-2026-50624) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a heap-use-after-free in the virtio-rng frontend when a delayed rng-random backend completion arrives after the virtio-rng device has been hot-unplugged. Fixes: CVE-2026-50624 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3917 Reported-by: Jia Jia Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Laurent Vivier Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260724094931.3005968-1-lvivier@redhat.com> --- backends/rng.c | 16 ++++++++++++++++ hw/virtio/virtio-rng.c | 2 ++ include/system/rng.h | 14 ++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/backends/rng.c b/backends/rng.c index ab94dfea85..7bed62616a 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -68,6 +68,22 @@ static void rng_backend_free_request(RngRequest *req) g_free(req); } +void rng_backend_cancel_requests(RngBackend *s, + EntropyReceiveFunc *receive_entropy, + const void *opaque) +{ + RngRequest *req, *next; + + QSIMPLEQ_FOREACH_SAFE(req, &s->requests, next, next) { + if (req->receive_entropy != receive_entropy || + req->opaque != opaque) { + continue; + } + QSIMPLEQ_REMOVE(&s->requests, req, RngRequest, next); + rng_backend_free_request(req); + } +} + static void rng_backend_free_requests(RngBackend *s) { RngRequest *req, *next; diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 66690a34dc..d68d901195 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -234,6 +234,8 @@ static void virtio_rng_device_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIORNG *vrng = VIRTIO_RNG(dev); + rng_backend_cancel_requests(vrng->rng, chr_read, vrng); + qemu_del_vm_change_state_handler(vrng->vmstate); timer_free(vrng->rate_limit_timer); virtio_del_queue(vdev, 0); diff --git a/include/system/rng.h b/include/system/rng.h index e383f87d20..a2667d75ef 100644 --- a/include/system/rng.h +++ b/include/system/rng.h @@ -86,4 +86,18 @@ void rng_backend_request_entropy(RngBackend *s, size_t size, * deleted. */ void rng_backend_finalize_request(RngBackend *s, RngRequest *req); + +/** + * rng_backend_cancel_requests: + * @s: the backend that created the request + * @receive_entropy: the function invoked when entropy is available + * @opaque: data passed to @receive_entropy + * + * This function is used by the front-end to cancel all requests to a + * given backend. Requests to cancel are identified by the receive_entropy + * function and the data passed to the function. + */ +void rng_backend_cancel_requests(RngBackend *s, + EntropyReceiveFunc *receive_entropy, + const void *opaque); #endif From 70074cc48f450e6c95881ac534e67013c8773cb2 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 15 Jul 2026 16:13:00 +0200 Subject: [PATCH 191/860] backends/rng: cap request size to avoid oversized allocation rng_backend_request_entropy() uses the requested size to allocate a buffer with g_malloc(). With virtio-rng, this size comes from guest-supplied descriptor lengths. A malicious guest can set a very large descriptor length, causing QEMU to attempt a multi-gigabyte allocation and abort. Cap the allocation to 64 KiB. The virtio-rng queue size is hardcoded to 8 entries, the EGD backend protocol limits requests to 255 bytes, the Linux kernel hwrng framework requests at most SMP_CACHE_BYTES per call (64 bytes on x86_64), and the Windows viorng driver uses a 4 KiB buffer. The worst legitimate case is 8 x 4 KiB = 32 KiB, so 64 KiB is well above any legitimate use. Fixes: 14417039653d ("virtio-rng: use virtqueue_get_avail_bytes, fix migration") Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3983 Reported-by: dong ling Signed-off-by: Laurent Vivier Reviewed-by: Thomas Huth Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260715141300.2295392-1-lvivier@redhat.com> --- backends/rng.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backends/rng.c b/backends/rng.c index 7bed62616a..bc9c7a5ab3 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -11,11 +11,14 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "system/rng.h" #include "qapi/error.h" #include "qemu/module.h" #include "qom/object_interfaces.h" +#define RNG_MAX_REQUEST_SIZE (64 * KiB) + void rng_backend_request_entropy(RngBackend *s, size_t size, EntropyReceiveFunc *receive_entropy, void *opaque) @@ -27,7 +30,7 @@ void rng_backend_request_entropy(RngBackend *s, size_t size, req = g_malloc(sizeof(*req)); req->offset = 0; - req->size = size; + req->size = MIN(size, RNG_MAX_REQUEST_SIZE); req->receive_entropy = receive_entropy; req->opaque = opaque; req->data = g_malloc(req->size); From 6682ea3391277e732a6d74c5758206ba834e1615 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 21 Jul 2026 09:44:24 -0400 Subject: [PATCH 192/860] virtio: avoid packed vring virtio_queue_empty() infinite loops (CVE-2026-16457) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Virtqueue handler functions in device emulation code often look something like this: while (!virtio_queue_empty(vq)) { ...pop and process virtqueue element... } virtio-blk, virtio-scsi, virtio-crypto, and vhost-shadow-virtqueue use this pattern. The device may break (i.e. hit an error that requires device reset) during the loop. virtio_queue_empty() returns 1 for broken split vrings but not for broken packed vrings, leading to an infinite loop. Adjust the packed vring behavior to match split vrings and avoid infinite loops. Fixes: CVE-2026-16457 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3968 Reported-by: Anatol Belski Signed-off-by: Stefan Hajnoczi Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Message-ID: <20260721134424.196337-1-stefanha@redhat.com> --- hw/virtio/virtio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 979c4065b7..daa5607338 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -763,6 +763,10 @@ static int virtio_queue_packed_empty_rcu(VirtQueue *vq) struct VRingPackedDesc desc; VRingMemoryRegionCaches *cache; + if (virtio_device_disabled(vq->vdev)) { + return 1; + } + if (unlikely(!vq->vring.desc)) { return 1; } From 8cbcf7579355f995340aa27ad197ef61c4969c06 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Jul 2026 17:27:04 +0100 Subject: [PATCH 193/860] hw/net/xilinx_axienet: Don't write checksums off end of packet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The xilinx_axienet device has ethernet checksum offloading, with a mode where the guest provides the offsets within the packet where the data to be checksummed starts, and where the final checksum should be written into the packet. We don't sanity check the TX_CSINSERT offset before writing the checksum data into it, which means the guest can pass us a value that is larger than the packet itself and cause us to write the checksum off the end of the buffer. We also don't explicitly check the TX_CSBEGIN offset; this doesn't currently cause any problems because we will pass a negative length to net_checksum_add() which does nothing, but it's a potential trap for the future if the type used for the length gets changed to be unsigned. Explicitly check the offsets. The datasheet doesn't say what happens if the guest misprograms this, so we choose to log an error and send the packet as-is. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3599 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-ID: <20260706162704.787495-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/xilinx_axienet.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 9f5f65ecfa..d35f4a847d 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -922,20 +922,27 @@ xilinx_axienet_data_stream_push(StreamSink *obj, uint8_t *buf, size_t size, if (s->hdr[0] & 1) { unsigned int start_off = s->hdr[1] >> 16; unsigned int write_off = s->hdr[1] & 0xffff; - uint32_t tmp_csum; - uint16_t csum; - tmp_csum = net_checksum_add(s->txpos - start_off, - buf + start_off); - /* Accumulate the seed. */ - tmp_csum += s->hdr[2] & 0xffff; + if (start_off > s->txpos || write_off + 2 > s->txpos) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: offsets outside packet, skipping checksum\n", + TYPE_XILINX_AXI_ENET); + } else { + uint32_t tmp_csum; + uint16_t csum; - /* Fold the 32bit partial checksum. */ - csum = net_checksum_finish(tmp_csum); + tmp_csum = net_checksum_add(s->txpos - start_off, + buf + start_off); + /* Accumulate the seed. */ + tmp_csum += s->hdr[2] & 0xffff; - /* Writeback. */ - buf[write_off] = csum >> 8; - buf[write_off + 1] = csum & 0xff; + /* Fold the 32bit partial checksum. */ + csum = net_checksum_finish(tmp_csum); + + /* Writeback. */ + buf[write_off] = csum >> 8; + buf[write_off + 1] = csum & 0xff; + } } qemu_send_packet(qemu_get_queue(s->nic), buf, s->txpos); From aba80ad9d853e436047899177f92334379de4537 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Sat, 25 Jul 2026 20:55:27 -0400 Subject: [PATCH 194/860] linux-user: fix guards for the fsmount(2) syscall series The fsopen(), fsconfig(), fsmount() and fspick() implementations are guarded by defined(NR_fsopen) rather than defined(__NR_fsopen). No such macro exists, so the guard is never true and the entire series compiles out. Guests calling any of the four get -ENOSYS, which for example makes systemd's credential setup fail with EXIT_CREDENTIALS for most units. The strace bits for fsconfig() have the same typo. Check if FSCONFIG_SET_FLAG is defined to avoid build errors in the strace code on some older distributions (Helge). Fixes: 767c32fe6983 ("linux-user: implement fsmount(2) series of syscalls") Fixes: 6e0aa9f6c731 ("linux-user/strace: add fsmount series of syscalls") Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/strace.c | 2 +- linux-user/strace.list | 2 +- linux-user/syscall.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/linux-user/strace.c b/linux-user/strace.c index 3a81cc95f4..bc43a95a77 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -4344,7 +4344,7 @@ print_statx(CPUArchState *cpu_env, const struct syscallname *name, } #endif -#if defined(TARGET_NR_fsconfig) && defined(NR_fsconfig) +#if defined(TARGET_NR_fsconfig) && defined(__NR_fsconfig) && defined(FSCONFIG_SET_FLAG) static void print_fsconfig_cmd_name(int cmd) { diff --git a/linux-user/strace.list b/linux-user/strace.list index e363892e0a..25ace05187 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1725,7 +1725,7 @@ #ifdef TARGET_NR_fsopen { TARGET_NR_fsopen, "fsopen", "%s(%s,%d)", NULL, NULL }, #endif -#if defined(TARGET_NR_fsconfig) && defined(NR_fsconfig) +#if defined(TARGET_NR_fsconfig) && defined(__NR_fsconfig) && defined(FSCONFIG_SET_FLAG) { TARGET_NR_fsconfig, "fsconfig", NULL, print_fsconfig, NULL }, #endif #ifdef TARGET_NR_fsmount diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 3da5530d42..740142825d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9740,7 +9740,7 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname, int, __to_dfd, const char *, __to_pathname, unsigned int, flag) #endif -#if defined(TARGET_NR_fsopen) && defined(NR_fsopen) +#if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) #define __NR_sys_fsopen __NR_fsopen _syscall2(int, sys_fsopen, const char *, fs_name, unsigned int, flags); #define __NR_sys_fsconfig __NR_fsconfig @@ -14485,7 +14485,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_map_shadow_stack(cpu_env, arg1, arg2, arg3); #endif -#if defined(TARGET_NR_fsopen) && defined(NR_fsopen) +#if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) case TARGET_NR_fsopen: { p = lock_user_string(arg1); From 11ed6b92130a3b075cd6528e991c78538685d56a Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 20 Jul 2026 22:11:33 +0200 Subject: [PATCH 195/860] hw/sd/sdhci: Extract uSDHC-specific quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Linux, the ESDHC_MIX_CTRL quirk is guarded by esdhc_is_usdhc() while the eSDHC code path uses the standard SDHC interface. Extract the quirk into a new `usdhc_write()` function. Fixes file system corruption on emulated i.MX53 where Linux' esdhc_is_usdhc() returns false. The same likely happens on e500 and imx25-pdk machines. Cc: qemu-stable@nongnu.org Fixes: 75e98bc4f859 ("hw/sd/sdhci: Add TYPE_FSL_ESDHC_BE") Reviewed-by: Bin Meng Signed-off-by: Bernhard Beschow Message-ID: <20260720201133.24796-3-shentey@gmail.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sdhci.c | 73 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index c86dfa281f..e58a610397 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1795,34 +1795,6 @@ esdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) sdhci_write(opaque, offset, value, size); break; - case ESDHC_MIX_CTRL: - /* - * So, when SD/MMC stack in Linux tries to write to "Transfer - * Mode Register", ESDHC i.MX quirk code will translate it - * into a write to ESDHC_MIX_CTRL, so we do the opposite in - * order to get where we started - * - * Note that Auto CMD23 Enable bit is located in a wrong place - * on i.MX, but since it is not used by QEMU we do not care. - * - * We don't want to call sdhci_write(.., SDHC_TRNMOD, ...) - * here because it will result in a call to - * sdhci_send_command(s) which we don't want. - * - */ - s->trnmod = value & UINT16_MAX; - break; - case SDHC_TRNMOD: - /* - * Similar to above, but this time a write to "Command - * Register" will be translated into a 4-byte write to - * "Transfer Mode register" where lower 16-bit of value would - * be set to zero. So what we do is fill those bits with - * cached value from s->trnmod and let the SDHCI - * infrastructure handle the rest - */ - sdhci_write(opaque, offset, val | s->trnmod, size); - break; case SDHC_BLKSIZE: /* * ESDHCI does not implement "Host SDMA Buffer Boundary", and @@ -1891,9 +1863,52 @@ static void fsl_esdhc_le_init(Object *obj) qdev_prop_set_uint8(dev, "sd-spec-version", 2); } +static void +usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + SDHCIState *s = SYSBUS_SDHCI(opaque); + uint32_t value = (uint32_t)val; + + switch (offset) { + case ESDHC_MIX_CTRL: + /* + * So, when SD/MMC stack in Linux tries to write to "Transfer + * Mode Register", uSDHC i.MX quirk code will translate it + * into a write to ESDHC_MIX_CTRL, so we do the opposite in + * order to get where we started. + * + * Note that Auto CMD23 Enable bit is located in a wrong place + * on i.MX, but since it is not used by QEMU we do not care. + * + * We don't want to call sdhci_write(.., SDHC_TRNMOD, ...) + * here because it will result in a call to + * sdhci_send_command(s) which we don't want. + * + */ + s->trnmod = value & UINT16_MAX; + break; + + case SDHC_TRNMOD: + /* + * Similar to above, but this time a write to "Command + * Register" will be translated into a 4-byte write to + * "Transfer Mode register" where lower 16-bit of value would + * be set to zero. So what we do is fill those bits with + * cached value from s->trnmod and let the SDHCI + * infrastructure handle the rest + */ + sdhci_write(opaque, offset, val | s->trnmod, size); + break; + + default: + esdhc_write(opaque, offset, val, size); + break; + } +} + static const MemoryRegionOps usdhc_mmio_ops = { .read = esdhc_read, - .write = esdhc_write, + .write = usdhc_write, .valid = { .min_access_size = 1, .max_access_size = 4, From b395a655ae0eb8d38c1e61a6bcaadb2e79326a5b Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 22 Jul 2026 13:24:48 +0200 Subject: [PATCH 196/860] hw/net/e1000e: recalculate rx_desc_len on migration load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rx_desc_len is migrated as a raw uint8_t from the stream, but it is a derived value that can be computed from the register state in core.mac[RFCTL] and core.mac[RCTL]. A crafted migration stream can set rx_desc_len to an invalid value (e.g. 64), causing a stack buffer overflow in e1000e_write_packet_to_guest() which copies rx_desc_len bytes into a 32-byte stack union. Recalculate rx_desc_len and other derived values from the register state in post_load, ignoring the untrusted values from the stream. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3869 Signed-off-by: Laurent Vivier Reviewed-by: Akihiko Odaki Message-ID: <20260722112449.1386162-2-lvivier@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/e1000e_core.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 46e156a5dd..b87a9f167a 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -1948,6 +1948,16 @@ e1000e_calc_rxdesclen(E1000ECore *core) trace_e1000e_rx_desc_len(core->rx_desc_len); } +static void +e1000e_calc_rxconf(E1000ECore *core) +{ + e1000e_parse_rxbufsize(core); + e1000e_calc_rxdesclen(core); + core->rxbuf_min_shift = + ((core->mac[RCTL] / E1000_RCTL_RDMTS_QUAT) & 3) + 1 + + E1000_RING_DESC_LEN_SHIFT; +} + static void e1000e_set_rx_control(E1000ECore *core, int index, uint32_t val) { @@ -1955,11 +1965,7 @@ e1000e_set_rx_control(E1000ECore *core, int index, uint32_t val) trace_e1000e_rx_set_rctl(core->mac[RCTL]); if (val & E1000_RCTL_EN) { - e1000e_parse_rxbufsize(core); - e1000e_calc_rxdesclen(core); - core->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1 + - E1000_RING_DESC_LEN_SHIFT; - + e1000e_calc_rxconf(core); e1000e_start_recv(core); } } @@ -3557,5 +3563,7 @@ e1000e_core_post_load(E1000ECore *core) e1000e_intrmgr_resume(core); e1000e_autoneg_resume(core); + e1000e_calc_rxconf(core); + return 0; } From 1e5efe6d93701d6b2b05b62822c02eee935490b5 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 22 Jul 2026 13:24:49 +0200 Subject: [PATCH 197/860] hw/net/igb: recalculate rx_desc_len on migration load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rx_desc_len is migrated as a raw uint8_t from the stream but is a derived value. Currently igb_rx_use_legacy_descriptor() is a stub that always returns false, so rx_desc_len is always set to sizeof(union e1000_adv_rx_desc). Recalculate it in post_load to prevent a crafted migration stream from setting an invalid value. Cc: qemu-stable@nongnu.org Signed-off-by: Laurent Vivier Reviewed-by: Akihiko Odaki Message-ID: <20260722112449.1386162-3-lvivier@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé --- hw/net/igb_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/net/igb_core.c b/hw/net/igb_core.c index 45d8fd795b..2a48839073 100644 --- a/hw/net/igb_core.c +++ b/hw/net/igb_core.c @@ -4548,5 +4548,7 @@ igb_core_post_load(IGBCore *core) igb_intrmgr_resume(core); igb_autoneg_resume(core); + igb_calc_rxdesclen(core); + return 0; } From d8813fdc4bdf3038cc55d03dab2d9dbbeb340226 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Sat, 25 Jul 2026 13:33:22 +0100 Subject: [PATCH 198/860] target/sparc: set reg window data structures currently after vmstate load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the SPARC CPU state, env->regwptr points into the env->regbase array at wherever the architectural CWP (current window pointer) says we are in the register windows. We don't migrate this directly, since it's a host pointer, so we must ensure it is set up again after migration load. We also have to deal with a special case when CWP is (nwindows - 1). In this case, while running we keep the "in" register data for this window in a temporary location at the end of the regbase[] array, so that generated code doesn't have to special case this "wrap around" case. In cpu_pre_save() we call cpu_set_cwp() to force a copy of the wrapped data from its temporary location into the architectural location in window 0's "out" registers. We then migrate only (nwindows * 16) entries in the regbase[] array. So on the destination we need to copy the "in" register data back to its temporary location again. For 32-bit SPARC we get this right, because the CWP is in the PSR. The get_psr() function does: env->cwp = 0; cpu_put_psr_raw(env, val); which causes cpu_put_psr_raw() to call cpu_set_cwp() in a way that sets up both regwptr and the wrapped-register data. However, for 64-bit SPARC the CWP is not in the PSR, and cpu_put_psr_raw() will not call cpu_set_cwp(). This leaves the guest register state in a corrupted state, and the guest will likely crash on the destination if it didn't happen to be executing with CWP == 0. Fix this by adding a custom vmstate_cwp VMStateInfo with corresponding get_cwp() and put_cwp() helpers which does the same for the 64-bit case. Cc: qemu-stable@nongnu.org Signed-off-by: Mark Cave-Ayland Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260725123411.993099-1-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- target/sparc/machine.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/target/sparc/machine.c b/target/sparc/machine.c index 5f402e098c..0d5d79b5e7 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -151,6 +151,37 @@ static const VMStateInfo vmstate_xcc = { .get = get_xcc, .put = put_xcc, }; + +static int get_cwp(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = qemu_get_be32(f); + + /* needed to ensure that the wrapping registers are correctly updated */ + env->cwp = 0; + cpu_set_cwp(env, val); + + return 0; +} + +static int put_cwp(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + SPARCCPU *cpu = opaque; + CPUSPARCState *env = &cpu->env; + uint32_t val = env->cwp; + + qemu_put_be32(f, val); + return 0; +} + +static const VMStateInfo vmstate_cwp = { + .name = "uint32", + .get = get_cwp, + .put = put_cwp, +}; #else static bool fq_needed(void *opaque) { @@ -286,7 +317,14 @@ const VMStateDescription vmstate_sparc_cpu = { VMSTATE_CPU_TIMER(env.hstick, SPARCCPU), /* On SPARC32 env.psrpil and env.cwp are migrated as part of the PSR */ VMSTATE_UINT32(env.psrpil, SPARCCPU), - VMSTATE_UINT32(env.cwp, SPARCCPU), + { + .name = "env.cwp", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_cwp, + .flags = VMS_SINGLE, + .offset = 0, + }, #endif VMSTATE_END_OF_LIST() }, From c607cca75c354f2be9c2d3259f5b5237afec1717 Mon Sep 17 00:00:00 2001 From: Shivang Upadhyay Date: Tue, 28 Jul 2026 13:16:12 +0530 Subject: [PATCH 199/860] tests/functional/ppc: skip remote interrupts test if -net user not built MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While running remote interrupts test, without libslirp-devel installed, facing the following panic logs. File ... raise VMLaunchFailure( ...<3 lines>... ) from exc ... Output: qemu-system-ppc64: -netdev user,id=net0: network backend 'user' is not compiled into this binary Adding netdev user requirement for this test. Suggested-by: Amit Machhiwal Reviewed-by: Amit Machhiwal Suggested-by: Thomas Huth Reviewed-by: Thomas Huth Fixes: 63f5ba80921 ("tests/functional: Add remote interrupts test for PowerNV") Signed-off-by: Shivang Upadhyay Tested-by: Amit Machhiwal Message-ID: <20260728074612.42397-1-shivangu@linux.ibm.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/functional/ppc64/test_powernv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/ppc64/test_powernv.py b/tests/functional/ppc64/test_powernv.py index bac2017e18..f3e05e4d38 100755 --- a/tests/functional/ppc64/test_powernv.py +++ b/tests/functional/ppc64/test_powernv.py @@ -90,6 +90,7 @@ class PowernvMachine(LinuxKernelTest): def test_linux_remote_interrupts(self): self.require_accelerator("tcg") + self.require_netdev('user') self.set_machine('powernv') # Have below setup in this test: From db3abd36e4c7c2a72e00e5806cf4c47f5aa5c491 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Mon, 27 Jul 2026 13:48:48 +0200 Subject: [PATCH 200/860] linux-user/sh4: allow full 32-bit address space On real SH4 hardware, the address space is split between user mode (U0, 0x00000000-0x7fffffff) and kernel mode (P1-P4, 0x80000000-0xffffffff), so TARGET_VIRT_ADDR_SPACE_BITS was set to 31 for CONFIG_USER_ONLY. However, qemu-user does not emulate the MMU, so this limit is not needed. The only effect is to restrict reserved_va to 2 GB, causing OOM failures for memory-intensive builds (e.g. webkit2gtk on Debian sh4 buildds). Set TARGET_VIRT_ADDR_SPACE_BITS to 32 unconditionally, like most other 32-bit targets. Also fix the TASK_UNMAPPED_BASE macro to use 1ull instead of 1u to avoid undefined behavior when shifting by 32. Reported-by: John Paul Adrian Glaubitz Signed-off-by: Laurent Vivier Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/sh4/target_mman.h | 2 +- target/sh4/cpu-param.h | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/linux-user/sh4/target_mman.h b/linux-user/sh4/target_mman.h index dd9016081e..2d05837c2f 100644 --- a/linux-user/sh4/target_mman.h +++ b/linux-user/sh4/target_mman.h @@ -1,6 +1,6 @@ /* arch/sh/include/asm/processor_32.h */ #define TASK_UNMAPPED_BASE \ - TARGET_PAGE_ALIGN((1u << TARGET_VIRT_ADDR_SPACE_BITS) / 3) + TARGET_PAGE_ALIGN((1ull << TARGET_VIRT_ADDR_SPACE_BITS) / 3) /* arch/sh/include/asm/elf.h */ #define ELF_ET_DYN_BASE (TASK_UNMAPPED_BASE * 2) diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index c3b8114e53..9d72e35e31 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -9,10 +9,8 @@ #define SH4_CPU_PARAM_H #define TARGET_PAGE_BITS 12 /* 4k */ -#ifdef CONFIG_USER_ONLY -# define TARGET_VIRT_ADDR_SPACE_BITS 31 -#else -# define TARGET_VIRT_ADDR_SPACE_BITS 32 -#endif + +/* qemu-user does not emulate the MMU, so no need to limit to 31 bits. */ +#define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif From 39a8c3941eb2b4550bb1339c340905ff3c341599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 201/860] hw/intc/arm_gicv3: Fix ARM_GICV3 dependency for KVM / WHPX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM and WHPX GICv3 implementations call gicv3_init_irqs_and_mmio() which is defined in arm_gicv3_common.c, itself build when ARM_GIC is selected. Both KVM and WHPX are conditional on ARM_GICV3, itself selecting ARM_GIC. Only build the ARM_GICV3 accelerator variants when ARM_GICV3 is selected. $ git grep -w gicv3_init_irqs_and_mmio hw/intc/arm_gicv3_common.c:314:void gicv3_init_irqs_and_mmio(GICv3State *s, hw/intc/arm_gicv3.c:450: gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); hw/intc/arm_gicv3_hvf.c:710: gicv3_init_irqs_and_mmio(s, hvf_gicv3_set_irq, NULL); hw/intc/arm_gicv3_kvm.c:822: gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); hw/intc/arm_gicv3_whpx.c:186: gicv3_init_irqs_and_mmio(s, whpx_gicv3_set_irq, NULL); Fixes: a7bf30342e6 ("hw/intc: Initial implementation of vGICv3") Fixes: e4c95f78a47 ("hw, target, accel: whpx: change apic_in_platform to kernel_irqchip") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20260721122135.6288-2-philmd@oss.qualcomm.com Signed-off-by: Peter Maydell --- hw/intc/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index fac2d228f9..377de78bb4 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -45,10 +45,10 @@ specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.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_WHPX', 'TARGET_AARCH64'], if_true: files('arm_gicv3_whpx.c')) +specific_ss.add(when: ['CONFIG_WHPX', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_whpx.c')) specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c')) stub_ss.add(files('arm_gicv3_hvf_stub.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_GIC_KVM', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.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')) From 48201bb01f9ea3584a3c1f553bd03e852e8b7215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 202/860] target/arm: Add stub for define_gicv5_cpuif_regs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The common helper.c file calls define_gicv5_cpuif_regs() which is only defined when TCG is built: $ git grep -w define_gicv5_cpuif_regs target/arm/internals.h:1885:void define_gicv5_cpuif_regs(ARMCPU *cpu); target/arm/helper.c:6334: define_gicv5_cpuif_regs(cpu); target/arm/tcg/gicv5-cpuif.c:923:void define_gicv5_cpuif_regs(ARMCPU *cpu) This fixes when building on macOS with --disable-tcg: Undefined symbols for architecture arm64: "_define_gicv5_cpuif_regs", referenced from: _register_cp_regs_for_features in target_arm_helper.c.o Define the GICv5 stubs in their own compilation unit, otherwise we get the following error: duplicate symbol '_raise_exception_ra' in: libsystem_arm.a.p/target_arm_tcg_op_helper.c.o libstubs_arm.a[4](target_arm_tcg-stubs.c.o) Fixes: ee0f1ce00db ("target/arm: GICv5 cpuif: Initial skeleton and GSB barrier insns") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20260721122135.6288-3-philmd@oss.qualcomm.com Signed-off-by: Peter Maydell --- target/arm/gicv5-stubs.c | 17 +++++++++++++++++ target/arm/meson.build | 1 + target/arm/tcg-stubs.c | 4 ---- 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 target/arm/gicv5-stubs.c diff --git a/target/arm/gicv5-stubs.c b/target/arm/gicv5-stubs.c new file mode 100644 index 0000000000..845626d7c6 --- /dev/null +++ b/target/arm/gicv5-stubs.c @@ -0,0 +1,17 @@ +/* + * QEMU ARM stubs for GICv5 TCG helper functions + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "internals.h" + +void define_gicv5_cpuif_regs(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void gicv5_update_ppi_state(CPUARMState *env, int ppi, bool level) +{ +} diff --git a/target/arm/meson.build b/target/arm/meson.build index 4412fde065..f6ae8934d1 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -37,6 +37,7 @@ arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) arm_user_ss.add(files('cpu.c')) arm_stubs_ss.add(files( 'cpu32-stubs.c', + 'gicv5-stubs.c', 'kvm-stub.c' )) arm_user_ss.add(files( diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index 8d97449cfc..aeeede8066 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -43,7 +43,3 @@ void vfp_clear_float_status_exc_flags(CPUARMState *env) void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { } - -void gicv5_update_ppi_state(CPUARMState *env, int ppi, bool level) -{ -} From b3a977da8fc12f21f4674b433b9ff34d4ecb62ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 203/860] target/arm: Only compile gicv5-cpuif.c when GICv5 is selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only compile gicv5-cpuif.c when ARM_GICV5 is Kconfig-selected, otherwise we get when building with --without-default-devices: Undefined symbols for architecture arm64: "_gicv5_activate", referenced from: _gicr_cdia_read in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_deactivate", referenced from: _gic_cddi_write in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_get_hppi", referenced from: _gic_hppi in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_request_config", referenced from: _gic_cdrcfg_write in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_set_enabled", referenced from: _gic_cddis_write in target_arm_tcg_gicv5-cpuif.c.o _gic_cden_write in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_set_handling", referenced from: _gic_cdhm_write in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_set_pending", referenced from: _gic_cdpend_write in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_set_priority", referenced from: _gic_cdpri_write in target_arm_tcg_gicv5-cpuif.c.o "_gicv5_set_target", referenced from: _gic_cdaff_write in target_arm_tcg_gicv5-cpuif.c.o Fixes: ee0f1ce00db ("target/arm: GICv5 cpuif: Initial skeleton and GSB barrier insns") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20260721122135.6288-4-philmd@oss.qualcomm.com Signed-off-by: Peter Maydell --- target/arm/tcg/meson.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 227e674ae2..cbfac0036e 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -91,6 +91,9 @@ arm_common_system_ss.add(files( 'tlb_helper.c', 'tlb-insns.c', )) +arm_common_system_ss.add(when: 'CONFIG_ARM_GICV5', if_true: files( + 'gicv5-cpuif.c', +)) arm_user_ss.add(files( 'tlb_helper.c', )) From e5ab18130a151ecd2295772e976a3dcf0ba2a8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 204/860] target/arm: Build TCG stubs as stub library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the stub library introduced in commit 0da978cdbc6 ("target/arm: define stub library") to prevent symbol conflicts. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20260721122135.6288-5-philmd@oss.qualcomm.com Signed-off-by: Peter Maydell --- target/arm/meson.build | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index f6ae8934d1..0369f96b4c 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -38,7 +38,8 @@ arm_user_ss.add(files('cpu.c')) arm_stubs_ss.add(files( 'cpu32-stubs.c', 'gicv5-stubs.c', - 'kvm-stub.c' + 'kvm-stub.c', + 'tcg-stubs.c', )) arm_user_ss.add(files( 'el2-stubs.c', @@ -65,8 +66,6 @@ subdir('whpx') if 'CONFIG_TCG' in config_all_accel subdir('tcg') -else - arm_common_system_ss.add(files('tcg-stubs.c')) endif arm_user_ss.add_all(arm_common_user_system_ss) From c4e23476936a96404f92a4b46cb3a7d8ad7dee4f Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 205/860] hw/net/can/flexcan: Use mbs[] array for FIFO pop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement FIFO entry shifting using the underlying `mbs[]` array instead of the overlapping `fifo` union view. This makes it explicit that the operation copies within a contiguous mailbox array and avoids Coverity CID 1662971. Reported-by: Peter Maydell Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260723070059.6332-2-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/can/flexcan.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index da36d10bd3..3a97edf7a4 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -879,22 +879,22 @@ static bool flexcan_can_receive(CanBusClientState *client) */ static void flexcan_fifo_pop(FlexcanState *s) { - if (s->regs.fifo.mb_back.can_ctrl != 0) { + if (s->regs.mbs[0].can_ctrl != 0) { /* move queue elements forward */ - memmove(&s->regs.fifo.mb_back, &s->regs.fifo.mbs_queue[0], - sizeof(s->regs.fifo.mbs_queue)); + memmove(&s->regs.mbs[0], &s->regs.mbs[1], + sizeof(s->regs.mbs[0]) * (FLEXCAN_FIFO_DEPTH - 1)); /* clear the first-in slot */ memset(&s->regs.mbs[FLEXCAN_FIFO_DEPTH - 1], 0, sizeof(FlexcanRegsMessageBuffer)); trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 1, - s->regs.fifo.mb_back.can_ctrl != 0); + s->regs.mbs[0].can_ctrl != 0); } else { trace_flexcan_fifo_pop(DEVICE(s)->canonical_path, 0, 0); } - if (s->regs.fifo.mb_back.can_ctrl != 0) { + if (s->regs.mbs[0].can_ctrl != 0) { flexcan_irq_iflag_set(s, I_FIFO_AVAILABLE); } else { flexcan_irq_iflag_clear(s, I_FIFO_AVAILABLE); From 8af2ced638eef6ffafecc5ec34c76eab149df5d3 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 206/860] hw/net/flexcan: Drop unused RX FIFO register overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the unused `FlexcanRegsRXFifo` union view now that FIFO accesses use the underlying `mbs[]` array directly. Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260723070059.6332-3-shentey@gmail.com Signed-off-by: Peter Maydell --- include/hw/net/flexcan.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h index 32de7b904a..d8159475e0 100644 --- a/include/hw/net/flexcan.h +++ b/include/hw/net/flexcan.h @@ -35,16 +35,6 @@ typedef struct FlexcanRegsMessageBuffer { uint32_t data[2]; } FlexcanRegsMessageBuffer; -/* RX FIFO view of message buffer registers */ -typedef struct FlexcanRegsRXFifo { - /* 6 message buffer deep queue, queue back first */ - FlexcanRegsMessageBuffer mb_back; - FlexcanRegsMessageBuffer mbs_queue[FLEXCAN_FIFO_DEPTH - 1]; - - /* number of filter elements active depends on ctrl2 | FLEXCAN_CTRL2_RFFN */ - uint32_t filter_table_els[128]; -} FlexcanRegsRXFifo; - /* FlexCAN register in hw layout */ typedef struct FlexcanRegs { uint32_t mcr; /* 0x00 */ @@ -78,7 +68,6 @@ typedef struct FlexcanRegs { union { /* 0x80 - not affected by soft reset */ uint32_t mb[sizeof(FlexcanRegsMessageBuffer) * FLEXCAN_MAILBOX_COUNT]; FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT]; - FlexcanRegsRXFifo fifo; }; uint32_t _reserved4[256]; /* 0x480 */ uint32_t rximr[64]; /* 0x880 - not affected by soft reset */ From 4ac969571b01c0de9d677dfe93647a5f78d44967 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 207/860] hw/net/can/flexcan: Fix out-of-bounds access in flexcan_mx_rx() Require `last_not_free_to_receive_mbid >= 0` before indexing `mbs[]`. This prevents a possible `-1` array index and fixes Coverity CID 1662790. Reported-by: Peter Maydell Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Peter Maydell Message-id: 20260723070059.6332-4-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/can/flexcan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index 3a97edf7a4..dd92a38337 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -1075,7 +1075,7 @@ static enum FlexcanRx flexcan_mb_rx(FlexcanState *s, const qemu_can_frame *buf) } } - if (last_not_free_to_receive_mbid >= -1) { + if (last_not_free_to_receive_mbid >= 0) { if (last_not_free_to_receive_locked) { /* * copy to temporary mailbox (SMB) From 9981b70ce799f24db94ccca2175217ecb3936a53 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 208/860] hw/net/can/flexcan: Fix mailbox index calculation in flexcan_mem_write() Calculate mailbox indices from the `mbs[]` array layout instead of the oversized raw `mb[]` view. This prevents accessing mailbox entries beyond the valid array range and fixes Coverity CID 1662974. Reported-by: Peter Maydell Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Peter Maydell Message-id: 20260723070059.6332-5-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/can/flexcan.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index dd92a38337..651904fa37 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -1151,6 +1151,8 @@ static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { FlexcanState *s = opaque; + const int mbid = (addr - offsetof(FlexcanRegs, mbs)) / + sizeof(s->regs.mbs[0]); uint32_t write_mask = ((const uint32_t *) &flexcan_regs_write_mask)[addr / 4]; uint32_t old_value = s->regs_raw[addr / 4]; @@ -1208,11 +1210,8 @@ static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val, default: s->regs_raw[addr / 4] = (val & write_mask) | (old_value & ~write_mask); - if (addr >= offsetof(FlexcanRegs, mb) && - addr < offsetof(FlexcanRegs, _reserved4)) { + if (0 <= mbid && mbid < ARRAY_SIZE(s->regs.mbs)) { /* access to mailbox */ - int mbid = (addr - offsetof(FlexcanRegs, mb)) / - sizeof(FlexcanRegsMessageBuffer); if (s->locked_mbidx == mbid) { flexcan_mb_unlock(s); From 8f8f86306fea9e8995207801f0a8efc959115e78 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 209/860] hw/net/can/flexcan: Fix mailbox index calculation in flexcan_mem_read() Calculate mailbox indices from the `mbs[]` array layout instead of the oversized raw `mb[]` view. This prevents accessing mailbox entries beyond the valid array range and fixes Coverity CID 1662974. Reported-by: Peter Maydell Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Peter Maydell Message-id: 20260723070059.6332-6-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/can/flexcan.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index 651904fa37..8d680f1832 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -1239,14 +1239,12 @@ static void flexcan_mem_write(void *opaque, hwaddr addr, uint64_t val, static uint64_t flexcan_mem_read(void *opqaue, hwaddr addr, unsigned size) { FlexcanState *s = opqaue; + const int mbid = (addr - offsetof(FlexcanRegs, mbs)) / + sizeof(s->regs.mbs[0]); uint32_t rv = s->regs_raw[addr >> 2]; - if (addr >= offsetof(FlexcanRegs, mb) && - addr < offsetof(FlexcanRegs, _reserved4)) { + if (0 <= mbid && mbid < ARRAY_SIZE(s->regs.mbs)) { /* reading from mailbox */ - hwaddr offset = addr - offsetof(FlexcanRegs, mb); - int mbid = offset / sizeof(FlexcanRegsMessageBuffer); - if (addr % 16 == 0 && s->locked_mbidx != mbid) { /* reading control word locks the mailbox */ flexcan_mb_unlock(s); From 7f780d9d2a125e61b9c741db96b1c7849ad42542 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 210/860] hw/net/can/flexcan: Fix mailbox reset mask initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mailbox reset mask initialized only the first word of the raw `mb[]` array. Fix the initialization to cover the whole mailbox, as indicated in the field description in the header. While at it, use the `mbs[]` view instead in order to drop the redundant and oversized `mb[]` view in the next commit. Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260723070059.6332-7-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/can/flexcan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index 8d680f1832..f84f090a7f 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -134,7 +134,11 @@ static const FlexcanRegs flexcan_regs_reset_mask = { ._reserved2 = 0, .dbg1 = 0, .dbg2 = 0, - .mb = {0xFFFFFFFF}, + .mbs = { [0 ... FLEXCAN_MAILBOX_COUNT - 1] = { + .can_ctrl = 0xFFFFFFFF, + .can_id = 0xFFFFFFFF, + .data = { 0xFFFFFFFF, 0xFFFFFFFF }, + } }, ._reserved4 = {0}, .rximr = {0xFFFFFFFF}, ._reserved5 = {0}, From 21d3af36b0adab8e56f19fcbbb206b8273611cba Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:46 +0100 Subject: [PATCH 211/860] hw/net/flexcan: Drop oversized 'mb[]' mailbox view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As indicated in the comments, the `mb[]` a.k.a. `mbs []` views should cover 0x400 (1024) bytes. However, the `mb[]` array covers four times the size since `sizeof(FlexcanRegsMessageBuffer)` returns the size in bytes instead of counting the number of uint32_t fields. This shifts the subsequent register offsets by 0xc00 which is not intended. Fix the size and thus the offsets by dropping the now unused `mb[]` view. Note that the different size changes the migration layout. Since the device model is new, no change in the version fields is needed. Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260723070059.6332-8-shentey@gmail.com Signed-off-by: Peter Maydell --- include/hw/net/flexcan.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h index d8159475e0..9affdff837 100644 --- a/include/hw/net/flexcan.h +++ b/include/hw/net/flexcan.h @@ -65,10 +65,8 @@ typedef struct FlexcanRegs { uint32_t dbg1; /* 0x58, unused */ uint32_t dbg2; /* 0x5C, unused */ uint32_t _reserved3[8]; /* 0x60 */ - union { /* 0x80 - not affected by soft reset */ - uint32_t mb[sizeof(FlexcanRegsMessageBuffer) * FLEXCAN_MAILBOX_COUNT]; - FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT]; - }; + /* 0x80 - not affected by soft reset */ + FlexcanRegsMessageBuffer mbs[FLEXCAN_MAILBOX_COUNT]; uint32_t _reserved4[256]; /* 0x480 */ uint32_t rximr[64]; /* 0x880 - not affected by soft reset */ uint32_t _reserved5[24]; /* 0x980 */ From cf942d0268d8f431ab0794f39700c3efe9210a06 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:47 +0100 Subject: [PATCH 212/860] hw/net/can/flexcan: Fix RXIMR reset mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The RXIMR registers are not affected by soft reset, so their reset mask must preserve all entries. Set the mask for the entire array instead of only the first register. Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260723070059.6332-9-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/can/flexcan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index f84f090a7f..205d0d6413 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -140,7 +140,7 @@ static const FlexcanRegs flexcan_regs_reset_mask = { .data = { 0xFFFFFFFF, 0xFFFFFFFF }, } }, ._reserved4 = {0}, - .rximr = {0xFFFFFFFF}, + .rximr = { [0 ... 63] = 0xFFFFFFFF }, ._reserved5 = {0}, .gfwr_mx6 = 0, ._reserved6 = {0}, From 62007626ac2d755b859bda231ca0ce8879a13636 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 27 Jul 2026 10:28:47 +0100 Subject: [PATCH 213/860] hw/net/can/flexcan: Remove RX SMB raw view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the existing `rx_smb0` message buffer view for register masks instead of the raw register array. Since all accesses now use the structured view, remove the redundant `rx_smb0_raw` union member. Signed-off-by: Bernhard Beschow Tested-by: Pavel Pisa Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260723070059.6332-10-shentey@gmail.com Signed-off-by: Peter Maydell --- hw/net/can/flexcan.c | 12 ++++++++++-- include/hw/net/flexcan.h | 5 +---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/hw/net/can/flexcan.c b/hw/net/can/flexcan.c index 205d0d6413..a8bad83cb8 100644 --- a/hw/net/can/flexcan.c +++ b/hw/net/can/flexcan.c @@ -106,7 +106,11 @@ static const FlexcanRegs flexcan_regs_write_mask = { .gfwr_mx6 = 0xFFFFFFFF, ._reserved6 = {0}, ._reserved8 = {0}, - .rx_smb0_raw = {0, 0, 0, 0}, + .rx_smb0 = { + .can_ctrl = 0, + .can_id = 0, + .data = { 0, 0 }, + }, .rx_smb1 = {0, 0, 0, 0}, }; static const FlexcanRegs flexcan_regs_reset_mask = { @@ -145,7 +149,11 @@ static const FlexcanRegs flexcan_regs_reset_mask = { .gfwr_mx6 = 0, ._reserved6 = {0}, ._reserved8 = {0}, - .rx_smb0_raw = {0, 0, 0, 0}, + .rx_smb0 = { + .can_ctrl = 0, + .can_id = 0, + .data = { 0, 0 }, + }, .rx_smb1 = {0, 0, 0, 0}, }; diff --git a/include/hw/net/flexcan.h b/include/hw/net/flexcan.h index 9affdff837..eee70bad42 100644 --- a/include/hw/net/flexcan.h +++ b/include/hw/net/flexcan.h @@ -81,10 +81,7 @@ typedef struct FlexcanRegs { uint32_t _rx14mask; /* 0xAA8 */ uint32_t _rx15mask; /* 0xAAC */ uint32_t tx_smb[4]; /* 0xAB0 */ - union { /* 0xAC0, used for SMB emulation */ - uint32_t rx_smb0_raw[4]; - FlexcanRegsMessageBuffer rx_smb0; - }; + FlexcanRegsMessageBuffer rx_smb0; /* 0xAC0, used for SMB emulation */ uint32_t rx_smb1[4]; /* 0xAD0 */ uint32_t mecr; /* 0xAE0 */ uint32_t erriar; /* 0xAE4 */ From 24506d7709970044462fcce743a86d72dc317359 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sat, 27 Jun 2026 14:32:08 +0800 Subject: [PATCH 214/860] docs/system/arm: Update Nuvoton OpenBMC image guidance Describe the current OpenBMC target naming and release status for Nuvoton machines. The latest 2.18.0 release no longer includes GSJ, so point users at older 2.14.0 sources and explain how to find Jenkins MTD artifacts. Signed-off-by: Bin Meng Message-id: 20260627063208.33642-1-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/system/arm/nuvoton.rst | 54 +++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst index e4827fb43a..51d36b2bc0 100644 --- a/docs/system/arm/nuvoton.rst +++ b/docs/system/arm/nuvoton.rst @@ -94,16 +94,60 @@ Boot options ------------ The Nuvoton machines can boot from an OpenBMC firmware image, or directly into -a kernel using the ``-kernel`` option. OpenBMC images for ``quanta-gsj`` and -possibly others can be downloaded from the OpenBMC jenkins : +a kernel using the ``-kernel`` option. OpenBMC machine names do not always +match QEMU machine names. Check the OpenBMC supported-machine list and Jenkins +for currently available source and pre-built images. + +Known OpenBMC (v2.18.0) target names for QEMU Nuvoton machines per: + + https://github.com/openbmc/openbmc/blob/2.18.0/meta-phosphor/docs/supported-machines.md + +.. list-table:: + :header-rows: 1 + + * - QEMU machine + - OpenBMC machine + * - ``npcm750-evb`` + - ``evb-npcm750`` + * - ``npcm845-evb`` + - ``evb-npcm845`` + * - ``quanta-gbs-bmc`` + - ``gbs`` + * - ``kudo-bmc`` + - ``kudo`` + * - ``mori-bmc`` + - ``mori`` + +As of June 2026, the latest OpenBMC release, ``2.18.0``, no longer lists a +``gsj`` machine. To build an image for QEMU's ``quanta-gsj`` machine, use an +older OpenBMC release that still contains ``meta-quanta/meta-gsj``. The +``2.14.0`` release contains the ``gsj`` machine: + + https://github.com/openbmc/openbmc/tree/2.14.0/meta-quanta/meta-gsj + +Some pre-built OpenBMC images for QEMU Nuvoton machines may be available on +Jenkins: https://jenkins.openbmc.org/ -The firmware image should be attached as an MTD drive. Example : +To find a pre-built MTD image on Jenkins, start from the Jenkins home page and +open the ``latest-master`` job. Select the matrix configuration whose +``target`` matches the OpenBMC machine name, for example +``label=docker-builder,target=gbs``, then open its latest successful build's +artifacts. The MTD image is usually published under +``openbmc/build/tmp/deploy/images//`` as +``obmc-phosphor-image--.static.mtd``. If Jenkins does not +list a matching target, or the build artifacts do not include an MTD image, +there is no current pre-built MTD image for that machine. + +The firmware image should be attached as an MTD drive. Example: .. code-block:: bash - $ qemu-system-arm -machine quanta-gsj -nographic \ - -drive file=image-bmc,if=mtd,bus=0,unit=0,format=raw + $ qemu-system-arm -machine quanta-gbs-bmc -nographic \ + -drive file=obmc-phosphor-image-gbs-xxxxxx.static.mtd,if=mtd,bus=0,unit=0,format=raw The default root password for test images is usually ``0penBmc``. + +For other machines that don't have pre-built images on Jenkins, build an image +from source by following the OpenBMC build documentation. From 1892a0cd43232754a3e55feb8edd028895f65d4b Mon Sep 17 00:00:00 2001 From: Simon Xu Date: Fri, 24 Jul 2026 11:09:42 -0500 Subject: [PATCH 215/860] hw/misc/mps2-scc.c: fix cfg7 write Change the CFG7 write function to modify the correct variable. This only affects the an536 machine currently as only it uses cfg7 to store the core 1 vector table base address. Signed-off-by: Simon Xu Reviewed-by: Richard Henderson Message-id: 20260724160943.52509-2-simonxhy0404@gmail.com Signed-off-by: Peter Maydell --- hw/misc/mps2-scc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index 7877b31479..1ecb2879e3 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -299,7 +299,7 @@ static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value, goto bad_offset; } /* AN536: Core 1 vector table base address */ - s->cfg6 = value; + s->cfg7 = value; break; case A_CFGDATA_OUT: s->cfgdata_out = value; From ccde37589fb4d171e540459f3dd1dc7987e86096 Mon Sep 17 00:00:00 2001 From: Simon Xu Date: Fri, 24 Jul 2026 11:09:43 -0500 Subject: [PATCH 216/860] hw/misc/mps2-scc.c: fix scc_partno field width Increase the extract32 length from 8 to 12. The Primary part number is defined as bits[15:4] by the "SSE-310 with M85 and U55 FPGA" documentation. For example for the mps3-an547, 0x547 is 12 bits but we only return 8 bits right now. Signed-off-by: Simon Xu Reviewed-by: Richard Henderson Message-id: 20260724160943.52509-3-simonxhy0404@gmail.com Signed-off-by: Peter Maydell --- hw/misc/mps2-scc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index 1ecb2879e3..554b504c72 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -57,7 +57,7 @@ REG32(ID, 0xFFC) static int scc_partno(MPS2SCC *s) { /* Return the partno field of the SCC_ID (0x524, 0x511, etc) */ - return extract32(s->id, 4, 8); + return extract32(s->id, 4, 12); } /* Is CFG_REG2 present? */ From c4580a3a6d1cc542479602048d15556a18928a22 Mon Sep 17 00:00:00 2001 From: botszhuang Date: Fri, 24 Jul 2026 20:42:20 +0800 Subject: [PATCH 217/860] hw/i2c/bcm2835_i2c: Correct CLKT register offset The Clock Stretch Timeout (CLKT) register for the BCM2835 I2C controller is actually located at offset 0x1c, not 0x20. Update the BCM2835_I2C_CLKT macro to match the hardware specification. Since QEMU's implementation ignores whatever value the guest writes to this register, and Linux only writes to CLKT and doesn't read it back, the main effect of this fix is to avoid an incorrect GUEST_ERROR log. Cc: qemu-stable@nongnu.org Fixes: 9cf3bc65afdb63 ("hw/i2c: Implement Broadcom Serial Controller (BSC)") Signed-off-by: botszhuang Tested-by: Nick Huang Message-id: 20260724124220.24152-1-22925483+botszhuang@users.noreply.github.com [PMM: removed commit message paragraph about effects which I think is incorrect, substituted a different one] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- include/hw/i2c/bcm2835_i2c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/i2c/bcm2835_i2c.h b/include/hw/i2c/bcm2835_i2c.h index 45f876df22..519c65f765 100644 --- a/include/hw/i2c/bcm2835_i2c.h +++ b/include/hw/i2c/bcm2835_i2c.h @@ -38,7 +38,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(BCM2835I2CState, BCM2835_I2C) #define BCM2835_I2C_FIFO 0x10 /* FIFO */ #define BCM2835_I2C_DIV 0x14 /* Clock Divider */ #define BCM2835_I2C_DEL 0x18 /* Data Delay */ -#define BCM2835_I2C_CLKT 0x20 /* Clock Stretch Timeout */ +#define BCM2835_I2C_CLKT 0x1c /* Clock Stretch Timeout */ #define BCM2835_I2C_C_I2CEN BIT(15) /* I2C enable */ #define BCM2835_I2C_C_INTR BIT(10) /* Interrupt on RXR */ From 4f635bddf50c8c7783dbab7a584ebce4a07feee2 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Jul 2026 18:33:22 +0100 Subject: [PATCH 218/860] hw/display/exynos4210_fimd: Factor out finding screen width/height MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we hard-code the expressions for getting the global screen width and height out of the VIDTCON2 register where we need them. Use functions instead. Make the global_width variable in exynos4210_fimd_update() uint32_t for consistency. (The values are clamped to well below INT_MAX, so there is no overflow risk here.) Stable CC because this is a prerequisite for an upcoming bugfix commit. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Marc-André Lureau Message-id: 20260706173324.804340-2-peter.maydell@linaro.org --- hw/display/exynos4210_fimd.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 7507e4fd3c..43ad5fa0b4 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1195,15 +1195,25 @@ static void exynos4210_fimd_update_irq(Exynos4210fimdState *s) } } +static uint32_t exynos4210_fimd_global_width(Exynos4210fimdState *s) +{ + return ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; +} + +static uint32_t exynos4210_fimd_global_height(Exynos4210fimdState *s) +{ + return ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & + FIMD_VIDTCON2_SIZE_MASK) + 1; +} + static void exynos4210_update_resolution(Exynos4210fimdState *s) { DisplaySurface *surface = qemu_console_surface(s->console); /* LCD resolution is stored in VIDEO TIME CONTROL REGISTER 2 */ - uint32_t width = ((s->vidtcon[2] >> FIMD_VIDTCON2_HOR_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; - uint32_t height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; + uint32_t width = exynos4210_fimd_global_width(s); + uint32_t height = exynos4210_fimd_global_height(s); if (s->ifb == NULL || surface_width(surface) != width || surface_height(surface) != height) { @@ -1229,14 +1239,14 @@ static bool exynos4210_fimd_update(void *opaque) bool blend = false; uint8_t *host_fb_addr; bool is_dirty = false; - int global_width; + uint32_t global_width; if (!s || !s->console || !s->enabled || surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) { return true; } - global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1; + global_width = exynos4210_fimd_global_width(s); exynos4210_update_resolution(s); surface = qemu_console_surface(s->console); From 7dd1e2218835f2c0bd754c092588110883a380a5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Jul 2026 18:33:23 +0100 Subject: [PATCH 219/860] hw/display/exynos4210_fimd: Pass width to draw_line functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The draw_line functions currently assume the width of the line they need to draw is w->rightbot_x - w->lefttop_x + 1, i.e. the full width of the guest-programmed window. We want to be able to clamp this to the overall screen size, which we can calculate in the calling function. Refactor to do this calculation in the caller and pass the width as an argument to the draw_line functions. Stable CC because this is a prerequisite for an upcoming bugfix commit. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Marc-André Lureau Message-id: 20260706173324.804340-3-peter.maydell@linaro.org --- hw/display/exynos4210_fimd.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 43ad5fa0b4..52f2285cf5 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -282,7 +282,7 @@ struct Exynos4210fimdWindow { pixel_to_rgb_func *pixel_to_rgb; void (*draw_line)(Exynos4210fimdWindow *w, uint8_t *src, uint8_t *dst, - bool blend); + uint32_t width, bool blend); uint32_t (*get_alpha)(Exynos4210fimdWindow *w, uint32_t pix_a); uint16_t lefttop_x, lefttop_y; /* VIDOSD0 register */ uint16_t rightbot_x, rightbot_y; /* VIDOSD1 register */ @@ -784,9 +784,9 @@ exynos4210_fimd_blend_pixel(Exynos4210fimdWindow *w, rgba p_bg, rgba *ret) /* Draw line with index in palette table in RAM frame buffer data */ #define DEF_DRAW_LINE_PALETTE(N) \ static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ + uint8_t *dst, uint32_t width, \ + bool blend) \ { \ - int width = w->rightbot_x - w->lefttop_x + 1; \ uint8_t *ifb = dst; \ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ uint64_t data; \ @@ -813,9 +813,8 @@ static void glue(draw_line_palette_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ /* Draw line with direct color value in RAM frame buffer data */ #define DEF_DRAW_LINE_NOPALETTE(N) \ static void glue(draw_line_, N)(Exynos4210fimdWindow *w, uint8_t *src, \ - uint8_t *dst, bool blend) \ + uint8_t *dst, uint32_t width, bool blend) \ { \ - int width = w->rightbot_x - w->lefttop_x + 1; \ uint8_t *ifb = dst; \ uint8_t swap = (w->wincon & FIMD_WINCON_SWAP) >> FIMD_WINCON_SWAP_SHIFT; \ uint64_t data; \ @@ -848,11 +847,10 @@ DEF_DRAW_LINE_NOPALETTE(32) /* Special draw line routine for window color map case */ static void draw_line_mapcolor(Exynos4210fimdWindow *w, uint8_t *src, - uint8_t *dst, bool blend) + uint8_t *dst, uint32_t width, bool blend) { rgba p, p_old; uint8_t *ifb = dst; - int width = w->rightbot_x - w->lefttop_x + 1; uint32_t map_color = w->winmap & FIMD_WINMAP_COLOR_MASK; do { @@ -1240,6 +1238,7 @@ static bool exynos4210_fimd_update(void *opaque) uint8_t *host_fb_addr; bool is_dirty = false; uint32_t global_width; + uint32_t window_width; if (!s || !s->console || !s->enabled || surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) { @@ -1255,6 +1254,8 @@ static bool exynos4210_fimd_update(void *opaque) if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { scrn_height = w->rightbot_y - w->lefttop_y + 1; scrn_width = w->virtpage_width; + /* Number of bytes to actually draw */ + window_width = w->rightbot_x - w->lefttop_x + 1; /* Total width of virtual screen page in bytes */ inc_size = scrn_width + w->virtpage_offsize; host_fb_addr = w->host_fb_addr; @@ -1273,7 +1274,8 @@ static bool exynos4210_fimd_update(void *opaque) last_line = line; w->draw_line(w, host_fb_addr, s->ifb + w->lefttop_x * RGBA_SIZE + (w->lefttop_y + line) * - global_width * RGBA_SIZE, blend); + global_width * RGBA_SIZE, + window_width, blend); } host_fb_addr += inc_size; fb_line_addr += inc_size; From a5f8d33682be079e6b9b41f07b2eb517121b3b46 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Jul 2026 18:33:24 +0100 Subject: [PATCH 220/860] hw/display/exynos4210_fimd: Clamp windows to screen size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In exynos4210_fimd_update(), we iterate through the enabled windows, blitting them to the screen. We assume here that the guest has not programmed the window's coordinates to be outside the overall LCD screen resulation, but we never check this. This can result in the guest being able to cause us to access outside our allocated framebuffer backing memory. Since all the coordinates here are unsigned, they can't be off the left/top side of the screen, only the bottom/right. If the top left corner of the window is out of bounds, the whole window is invisible and we can skip it. If the bottom right corner is out of bounds, we clamp it to the screen size so that we only draw the visible part. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3795 Signed-off-by: Peter Maydell Reviewed-by: Marc-André Lureau Message-id: 20260706173324.804340-4-peter.maydell@linaro.org --- hw/display/exynos4210_fimd.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 52f2285cf5..6c062a28c0 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1237,7 +1237,7 @@ static bool exynos4210_fimd_update(void *opaque) bool blend = false; uint8_t *host_fb_addr; bool is_dirty = false; - uint32_t global_width; + uint32_t global_width, global_height; uint32_t window_width; if (!s || !s->console || !s->enabled || @@ -1246,16 +1246,28 @@ static bool exynos4210_fimd_update(void *opaque) } global_width = exynos4210_fimd_global_width(s); + global_height = exynos4210_fimd_global_height(s); exynos4210_update_resolution(s); surface = qemu_console_surface(s->console); for (i = 0; i < NUM_OF_WINDOWS; i++) { w = &s->window[i]; if ((w->wincon & FIMD_WINCON_ENWIN) && w->host_fb_addr) { - scrn_height = w->rightbot_y - w->lefttop_y + 1; + uint32_t rightbot_x, rightbot_y; + + if (w->lefttop_x >= global_width || + w->lefttop_y >= global_height) { + /* Guest has put the window entirely offscreen: ignore */ + continue; + } + + /* Clamp right corner coords to be within the screen */ + rightbot_x = MIN(w->rightbot_x, global_width - 1); + rightbot_y = MIN(w->rightbot_y, global_height - 1); + scrn_height = rightbot_y - w->lefttop_y + 1; scrn_width = w->virtpage_width; /* Number of bytes to actually draw */ - window_width = w->rightbot_x - w->lefttop_x + 1; + window_width = rightbot_x - w->lefttop_x + 1; /* Total width of virtual screen page in bytes */ inc_size = scrn_width + w->virtpage_offsize; host_fb_addr = w->host_fb_addr; From af777e817a7c16d4eaf02989e838e8e0aaf173c5 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:55 +0200 Subject: [PATCH 221/860] parallels: read header/BAT table in bounded chunks parallels_open() read the whole header+BAT table with a single bdrv_pread() call sized s->header_size. For an image whose catalog approaches the "Catalog too large" bound (INT_MAX / sizeof(uint32_t) entries), that size approaches BDRV_REQUEST_MAX_BYTES, and the block layer legitimately refuses a single request that large, so the image failed to open with a generic I/O error even though the catalog size itself is within the format's documented limit. Read the header and BAT table in fixed-size chunks instead, so the maximum catalog size parallels_open() can actually address matches the bound it already enforces, independent of the file's block-layer alignment requirements. Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels.c | 17 ++++++++++---- tests/qemu-iotests/tests/parallels-checks | 23 +++++++++++++++++++ tests/qemu-iotests/tests/parallels-checks.out | 11 +++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index 59f00c64a6..0f655b58d5 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -52,6 +52,7 @@ #define HEADER_VERSION 2 #define HEADER_INUSE_MAGIC (0x746F6E59) #define MAX_PARALLELS_IMAGE_FACTOR (1ull << 32) +#define PARALLELS_HEADER_READ_CHUNK (64 * 1024 * 1024) static QEnumLookup prealloc_mode_lookup = { .array = (const char *const[]) { @@ -1241,7 +1242,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, BDRVParallelsState *s = bs->opaque; ParallelsHeader ph; int ret, i; - uint32_t size; + uint32_t size, header_off; int64_t file_nb_sectors, sector; uint32_t data_start; bool need_check = false; @@ -1311,9 +1312,17 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return -ENOMEM; } - ret = bdrv_pread(bs->file, 0, s->header_size, s->header, 0); - if (ret < 0) { - goto fail; + /* A single request s->header_size large exceeds BDRV_REQUEST_MAX_BYTES. */ + for (header_off = 0; header_off < s->header_size; + header_off += PARALLELS_HEADER_READ_CHUNK) { + uint32_t chunk = MIN(s->header_size - header_off, + PARALLELS_HEADER_READ_CHUNK); + + ret = bdrv_pread(bs->file, header_off, chunk, + (uint8_t *)s->header + header_off, 0); + if (ret < 0) { + goto fail; + } } s->bat_bitmap = (uint32_t *)(s->header + 1); diff --git a/tests/qemu-iotests/tests/parallels-checks b/tests/qemu-iotests/tests/parallels-checks index b281246a42..9535024885 100755 --- a/tests/qemu-iotests/tests/parallels-checks +++ b/tests/qemu-iotests/tests/parallels-checks @@ -44,6 +44,7 @@ _supported_os Linux SIZE=$((4 * 1024 * 1024)) IMGFMT=parallels CLUSTER_SIZE_OFFSET=28 +BAT_ENTRIES_OFFSET=32 DATA_OFF_OFFSET=48 BAT_OFFSET=64 @@ -199,6 +200,28 @@ _check_test_img -r all echo "== check first cluster ==" { $QEMU_IO -r -c "read -P 0x55 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +# Clear image +_make_test_img $SIZE + +echo "== TEST HUGE BAT TABLE OPEN ==" + +# Overflows a single read request, but stays under parallels_open()'s +# own catalog-size cap. +BAT_ENTRIES=536870896 +HEADER_SIZE=$((64 + 4 * BAT_ENTRIES)) + +echo "== advertise a BAT table larger than BDRV_REQUEST_MAX_BYTES ==" +poke_file "$TEST_IMG" "$BAT_ENTRIES_OFFSET" "\xf0\xff\xff\x1f" + +echo "== grow the file to match, without writing real data ==" +truncate -s $HEADER_SIZE "$TEST_IMG" + +echo "== open must succeed: the header/BAT read is chunked ==" +_img_info + +echo "== an unallocated cluster still reads as zeroes ==" +{ $QEMU_IO -r -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/tests/parallels-checks.out b/tests/qemu-iotests/tests/parallels-checks.out index 9793423111..6fb2014e8e 100644 --- a/tests/qemu-iotests/tests/parallels-checks.out +++ b/tests/qemu-iotests/tests/parallels-checks.out @@ -129,4 +129,15 @@ No errors were found on the image. == check first cluster == read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST HUGE BAT TABLE OPEN == +== advertise a BAT table larger than BDRV_REQUEST_MAX_BYTES == +== grow the file to match, without writing real data == +== open must succeed: the header/BAT read is chunked == +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 4 MiB (4194304 bytes) +== an unallocated cluster still reads as zeroes == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done From 3a1e0618ae3df56128a42f98b6168a4c85598466 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:55 +0200 Subject: [PATCH 222/860] parallels: fix bat_entries overflow in image creation parallels_co_create() computed the BAT entry count directly into a uint32_t, wrapping silently to zero at exactly 2^32 entries and writing out a header whose BAT no longer matches its advertised size. Compute it in an int64_t first and reject it once it no longer fits, matching the cap parallels_open() already enforces. Also reject cluster-size 0, and clamp header.cylinders instead of letting it truncate the same way. Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels.c | 23 +++++++++++++++++------ tests/qemu-iotests/212 | 8 ++++++-- tests/qemu-iotests/212.out | 10 ++++++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index 0f655b58d5..e3d26a6650 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -999,7 +999,8 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) BlockdevCreateOptionsParallels *parallels_opts; BlockDriverState *bs; BlockBackend *blk; - int64_t total_size, cl_size; + int64_t total_size, cl_size, bat_count; + uint64_t cylinders; uint32_t bat_entries, bat_sectors; ParallelsHeader header; uint8_t tmp[BDRV_SECTOR_SIZE]; @@ -1017,16 +1018,22 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) cl_size = DEFAULT_CLUSTER_SIZE; } - /* XXX What is the real limit here? This is an insanely large maximum. */ + /* Bounds cl_size so the multiplication below can't overflow int64_t. */ if (cl_size >= INT64_MAX / MAX_PARALLELS_IMAGE_FACTOR) { error_setg(errp, "Cluster size is too large"); return -EINVAL; } - if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) { + if (cl_size <= 0 || total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) { error_setg(errp, "Image size is too large for this cluster size"); return -E2BIG; } + bat_count = DIV_ROUND_UP(total_size, cl_size); + if (bat_count > INT_MAX / (int64_t)sizeof(uint32_t)) { + error_setg(errp, "Catalog too large"); + return -EFBIG; + } + if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) { error_setg(errp, "Image size must be a multiple of 512 bytes"); return -EINVAL; @@ -1052,7 +1059,7 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) blk_set_allow_write_beyond_eof(blk, true); /* Create image format */ - bat_entries = DIV_ROUND_UP(total_size, cl_size); + bat_entries = bat_count; bat_sectors = DIV_ROUND_UP(bat_entry_off(bat_entries), cl_size); bat_sectors = (bat_sectors * cl_size) >> BDRV_SECTOR_BITS; @@ -1061,8 +1068,12 @@ parallels_co_create(BlockdevCreateOptions* opts, Error **errp) header.version = cpu_to_le32(HEADER_VERSION); /* don't care much about geometry, it is not used on image level */ header.heads = cpu_to_le32(HEADS_NUMBER); - header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE - / HEADS_NUMBER / SEC_IN_CYL); + cylinders = total_size / BDRV_SECTOR_SIZE / HEADS_NUMBER / SEC_IN_CYL; + /* Write only by spec, do not care */ + if (cylinders >= UINT32_MAX) { + cylinders = UINT32_MAX; + } + header.cylinders = cpu_to_le32(cylinders); header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS); header.bat_entries = cpu_to_le32(bat_entries); header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE)); diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index d4af0c4ac8..4ca6149b6b 100755 --- a/tests/qemu-iotests/212 +++ b/tests/qemu-iotests/212 @@ -133,13 +133,15 @@ with iotests.FilePath('t.parallels') as disk_path, \ # # Maximum size # + # Largest catalog parallels_open() can address. + # iotests.log("=== Maximum size ===") iotests.log("") vm.launch() vm.blockdev_create({ 'driver': imgfmt, 'file': 'node0', - 'size': 4503599627369984}) + 'size': 562949952372736}) vm.shutdown() iotests.img_info_log(disk_path) @@ -158,13 +160,15 @@ with iotests.FilePath('t.parallels') as disk_path, \ # 4. 2^63 - 512 (generally valid, but with the image header the file will # exceed 63 bits) # 5. 2^52 (512 bytes more than maximum image size) + # 6. 2^52 - 512 (wraps bat_entries to 0 at the default 1 MiB cluster size) iotests.log("=== Invalid sizes ===") iotests.log("") vm.launch() for size in [ 1234, 18446744073709551104, 9223372036854775808, - 9223372036854775296, 4503599627370497 ]: + 9223372036854775296, 4503599627370497, + 4503599627369984 ]: vm.blockdev_create({ 'driver': imgfmt, 'file': 'node0', 'size': size }) diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out index 8102033488..d59f8ed44b 100644 --- a/tests/qemu-iotests/212.out +++ b/tests/qemu-iotests/212.out @@ -69,14 +69,14 @@ virtual size: 0 B (0 bytes) === Maximum size === -{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 562949952372736}}} {"return": {}} {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} image: TEST_IMG file format: IMGFMT -virtual size: 4 PiB (4503599627369984 bytes) +virtual size: 512 TiB (562949952372736 bytes) === Invalid sizes === @@ -110,6 +110,12 @@ Job failed: Image size is too large for this cluster size {"execute": "job-dismiss", "arguments": {"id": "job0"}} {"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}} +{"return": {}} +Job failed: Catalog too large +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + === Invalid cluster size === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1234, "driver": "parallels", "file": "node0", "size": 67108864}}} From 26c871c76885b1e03786d2cd6da090bc30e8faf2 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:55 +0200 Subject: [PATCH 223/860] parallels: reject BAT entries pointing outside backed storage parallels_open()'s BAT scan and parallels_check_outside_image() only checked entries against the file's upper end, matching just half of what docs/interop/parallels.rst requires: an entry's offset must be both >= data_start and < the file size. An entry below data_start resolves into the header/BAT region itself, corrupting metadata on write or losing the write silently on a partial overlap, and neither qemu-img check nor the open-time scan ever caught it. Check both bounds everywhere a BAT entry is resolved to a host offset: seek_to_sector(), the open-time scan (without letting a bad entry inflate data_end), and parallels_check_outside_image(). Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels.c | 27 +++++++-- tests/qemu-iotests/tests/parallels-checks | 58 +++++++++++++++++++ tests/qemu-iotests/tests/parallels-checks.out | 35 +++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/block/parallels.c b/block/parallels.c index e3d26a6650..8a7e8b4aba 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -119,6 +119,7 @@ static uint32_t bat_entry_off(uint32_t idx) static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num) { uint32_t index, offset; + int64_t cluster_off; index = sector_num / s->tracks; offset = sector_num % s->tracks; @@ -127,7 +128,14 @@ static int64_t seek_to_sector(BDRVParallelsState *s, int64_t sector_num) if ((index >= s->bat_size) || (s->bat_bitmap[index] == 0)) { return -1; } - return bat2sect(s, index) + offset; + + cluster_off = bat2sect(s, index); + if (cluster_off < s->data_start || cluster_off + s->tracks > s->data_end) { + /* Cluster is outside of the image file or overlaps the header. */ + return -1; + } + + return cluster_off + offset; } static int cluster_remainder(BDRVParallelsState *s, int64_t sector_num, @@ -703,18 +711,22 @@ parallels_check_outside_image(BlockDriverState *bs, BdrvCheckResult *res, { BDRVParallelsState *s = bs->opaque; uint32_t i; - int64_t off, high_off, size; + int64_t off, high_off, size, data_start_off; size = bdrv_co_getlength(bs->file->bs); if (size < 0) { res->check_errors++; return size; } + data_start_off = s->data_start << BDRV_SECTOR_BITS; high_off = 0; for (i = 0; i < s->bat_size; i++) { off = bat2sect(s, i) << BDRV_SECTOR_BITS; - if (off + s->cluster_size > size) { + if (off == 0) { + continue; + } + if (off < data_start_off || off + s->cluster_size > size) { fprintf(stderr, "%s cluster %u is outside image\n", fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR", i); res->corruptions++; @@ -1398,11 +1410,18 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, for (i = 0; i < s->bat_size; i++) { sector = bat2sect(s, i); + if (sector == 0) { + continue; /* not allocated */ + } + if (sector < data_start || sector + s->tracks > file_nb_sectors) { + /* Cluster is outside of the image file or overlaps the header. */ + need_check = true; + continue; + } if (sector + s->tracks > s->data_end) { s->data_end = sector + s->tracks; } } - need_check = need_check || s->data_end > file_nb_sectors; if (!need_check) { ret = parallels_fill_used_bitmap(bs); diff --git a/tests/qemu-iotests/tests/parallels-checks b/tests/qemu-iotests/tests/parallels-checks index 9535024885..abd119bc7b 100755 --- a/tests/qemu-iotests/tests/parallels-checks +++ b/tests/qemu-iotests/tests/parallels-checks @@ -222,6 +222,64 @@ _img_info echo "== an unallocated cluster still reads as zeroes ==" { $QEMU_IO -r -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir +# Clear image +_make_test_img $SIZE + +echo "== TEST BAT ENTRY POINTING OUTSIDE IMAGE ==" + +echo "== corrupt image: point first cluster far outside the file ==" +poke_file_le "$TEST_IMG" $BAT_OFFSET 4 1000000 + +echo "== read-only read must return zeroes, not an I/O error ==" +{ $QEMU_IO -r -c "read -P 0x00 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== write must allocate a fresh cluster instead of trusting the entry ==" +{ $QEMU_IO -c "write -P 0x77 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== file did not grow anywhere near the bogus offset ==" +file_size=`stat --printf="%s" "$TEST_IMG"` +if [ "$file_size" -lt $((16 * 1024 * 1024)) ]; then + echo "file size sane: yes" +else + echo "file size sane: no ($file_size bytes)" +fi + +echo "== data reads back correctly ==" +{ $QEMU_IO -r -c "read -P 0x77 0 $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image, with a small cluster size so the BAT table itself spans +# more than one cluster and there is room to point before data_off. +_make_test_img -o cluster_size=512 65536 + +SMALL_CLUSTER_SIZE=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4) +SMALL_CLUSTER_SIZE=$((SMALL_CLUSTER_SIZE * 512)) +DATA_OFF=$(peek_file_le $TEST_IMG $DATA_OFF_OFFSET 4) +echo "cluster size: $SMALL_CLUSTER_SIZE, data offset (sectors): $DATA_OFF" + +# Cluster index 1 starts at this byte offset, which must be < data_off +# in sectors * 512 for this test to actually exercise the bug. +VICTIM_OFFSET=$SMALL_CLUSTER_SIZE + +echo "== TEST BAT ENTRY POINTING BEFORE DATA AREA ==" + +echo "== corrupt image: point first cluster into the BAT table itself ==" +poke_file_le "$TEST_IMG" $BAT_OFFSET 4 1 + +echo "== qemu-img check detects it without repairing ==" +_check_test_img + +echo "== bytes at the victim offset before write ==" +echo "$(peek_file_le "$TEST_IMG" $VICTIM_OFFSET 4)" + +echo "== write must allocate a fresh cluster instead of clobbering the BAT ==" +{ $QEMU_IO -c "write -P 0x88 0 $SMALL_CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo "== bytes at the victim offset are unchanged ==" +echo "$(peek_file_le "$TEST_IMG" $VICTIM_OFFSET 4)" + +echo "== data reads back correctly ==" +{ $QEMU_IO -r -c "read -P 0x88 0 $SMALL_CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/tests/parallels-checks.out b/tests/qemu-iotests/tests/parallels-checks.out index 6fb2014e8e..914616d023 100644 --- a/tests/qemu-iotests/tests/parallels-checks.out +++ b/tests/qemu-iotests/tests/parallels-checks.out @@ -140,4 +140,39 @@ virtual size: 4 MiB (4194304 bytes) == an unallocated cluster still reads as zeroes == read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST BAT ENTRY POINTING OUTSIDE IMAGE == +== corrupt image: point first cluster far outside the file == +== read-only read must return zeroes, not an I/O error == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== write must allocate a fresh cluster instead of trusting the entry == +Repairing cluster 0 is outside image +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== file did not grow anywhere near the bogus offset == +file size sane: yes +== data reads back correctly == +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65536 +cluster size: 512, data offset (sectors): 2 +== TEST BAT ENTRY POINTING BEFORE DATA AREA == +== corrupt image: point first cluster into the BAT table itself == +== qemu-img check detects it without repairing == +ERROR cluster 0 is outside image + +1 errors were found on the image. +Data may be corrupted, or further writes to the image may corrupt it. +== bytes at the victim offset before write == +0 +== write must allocate a fresh cluster instead of clobbering the BAT == +Repairing cluster 0 is outside image +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== bytes at the victim offset are unchanged == +0 +== data reads back correctly == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) *** done From ac52279673625b48e14243c4121bacf47efbed79 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:56 +0200 Subject: [PATCH 224/860] parallels: validate bitmap L1 table size before allocating it parallels_load_bitmap() allocated the L1 table sized directly from the untrusted l1_size field, only cross-checking it against the bitmap's actual size after the allocation and the L1 table copy had already happened. Compute the expected size and reject a mismatch before touching the allocator, instead of after. Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels-ext.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 3410daa620..97744c9696 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -70,20 +70,11 @@ parallels_load_bitmap_data(BlockDriverState *bs, const uint64_t *l1_table, uint64_t offset, limit; uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap); uint8_t *buf = NULL; - uint64_t i, tab_size = - DIV_ROUND_UP(bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size), - s->cluster_size); - - if (tab_size != l1_size) { - error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond " - "to bitmap size and cluster size. Expected %" PRIu64, - l1_size, tab_size); - return -EINVAL; - } + uint64_t i; buf = qemu_blockalign(bs, s->cluster_size); limit = bdrv_dirty_bitmap_serialization_coverage(s->cluster_size, bitmap); - for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) { + for (i = 0, offset = 0; i < l1_size; ++i, offset += limit) { uint64_t count = MIN(bm_size - offset, limit); uint64_t entry = l1_table[i]; @@ -124,12 +115,14 @@ static BdrvDirtyBitmap * GRAPH_RDLOCK parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, Error **errp) { + BDRVParallelsState *s = bs->opaque; int ret; ParallelsDirtyBitmapFeature bf; g_autofree uint64_t *l1_table = NULL; BdrvDirtyBitmap *bitmap; QemuUUID uuid; char uuidstr[UUID_STR_LEN]; + uint64_t bm_size, tab_size; int i; if (data_size < sizeof(bf)) { @@ -164,6 +157,17 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, return NULL; } + bm_size = bdrv_dirty_bitmap_size(bitmap); + tab_size = DIV_ROUND_UP( + bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size), + s->cluster_size); + if (tab_size != bf.l1_size) { + error_setg(errp, "Bitmap table size %" PRIu32 " does not correspond " + "to bitmap size and cluster size. Expected %" PRIu64, + bf.l1_size, tab_size); + goto fail; + } + l1_table = g_new(uint64_t, bf.l1_size); for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { l1_table[i] = ldq_le_p(data); @@ -171,8 +175,7 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp); if (ret < 0) { - bdrv_release_dirty_bitmap(bitmap); - return NULL; + goto fail; } /* We support format extension only for RO parallels images. */ @@ -180,6 +183,10 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, bdrv_dirty_bitmap_set_readonly(bitmap, true); return bitmap; + +fail: + bdrv_release_dirty_bitmap(bitmap); + return NULL; } static int GRAPH_RDLOCK From d5250d7ed772d704d7143c94227979c5518a2f40 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:56 +0200 Subject: [PATCH 225/860] parallels: skip loading a genuinely empty bitmap L1 table parallels_load_bitmap_data() unconditionally calls bdrv_dirty_bitmap_deserialize_finish() even when there is nothing to deserialize, which hits an assertion in hbitmap (hbitmap_iter_init: 'pos < hb->size') when the bitmap itself has zero size, i.e. the disk is a zero-sector image. Skip allocating, populating and loading the L1 table entirely when l1_size == 0. This is safe only because the previous commit already guarantees l1_size == 0 exclusively means the disk has 0 size. Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels-ext.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 97744c9696..704e16e1de 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -168,14 +168,17 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, goto fail; } - l1_table = g_new(uint64_t, bf.l1_size); - for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { - l1_table[i] = ldq_le_p(data); - } + if (bf.l1_size != 0) { + l1_table = g_new(uint64_t, bf.l1_size); + for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { + l1_table[i] = ldq_le_p(data); + } - ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, errp); - if (ret < 0) { - goto fail; + ret = parallels_load_bitmap_data(bs, l1_table, bf.l1_size, bitmap, + errp); + if (ret < 0) { + goto fail; + } } /* We support format extension only for RO parallels images. */ From 4b620302ea86d71cc454d8b09a4235e46d6473ab Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:56 +0200 Subject: [PATCH 226/860] parallels: avoid fatal abort on large bitmap L1 table parallels_load_bitmap() allocated the L1 table with g_new(), which aborts the whole process on allocation failure instead of returning an error. Use g_try_new() and fail the open normally. Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels-ext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/block/parallels-ext.c b/block/parallels-ext.c index 704e16e1de..7f6ab6b0d2 100644 --- a/block/parallels-ext.c +++ b/block/parallels-ext.c @@ -169,7 +169,13 @@ parallels_load_bitmap(BlockDriverState *bs, uint8_t *data, size_t data_size, } if (bf.l1_size != 0) { - l1_table = g_new(uint64_t, bf.l1_size); + l1_table = g_try_new(uint64_t, bf.l1_size); + if (!l1_table) { + error_setg(errp, "Failed to allocate the bitmap L1 table " + "(%" PRIu32 " entries)", bf.l1_size); + goto fail; + } + for (i = 0; i < bf.l1_size; i++, data += sizeof(uint64_t)) { l1_table[i] = ldq_le_p(data); } From e8a28c2e0e1faf20713938f549fa4c9d9a94cebb Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:54:56 +0200 Subject: [PATCH 227/860] parallels: validate BAT capacity against advertised disk size parallels_open() copied nb_sectors, tracks, and bat_entries from the image header without checking that the BAT actually covers the advertised virtual disk size. An image whose header claims more sectors than its BAT covers passes the generic block-layer bounds check on open. A write into the gap between BAT coverage and the advertised size then reaches allocate_clusters(), whose internal assert(idx < s->bat_size && idx + to_allocate <= s->bat_size) aborts the process instead of returning a normal I/O error. Reject such images at open time by requiring bat_size * tracks >= total_sectors, matching the invariant that allocate_clusters() already assumes. Reported-by: Feifan Qian Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3804 Signed-off-by: Denis V. Lunev CC: Thomas Huth CC: Stefan Hajnoczi --- block/parallels.c | 6 ++++++ tests/qemu-iotests/tests/parallels-checks | 21 +++++++++++++++++++ tests/qemu-iotests/tests/parallels-checks.out | 7 +++++++ 3 files changed, 34 insertions(+) diff --git a/block/parallels.c b/block/parallels.c index 8a7e8b4aba..93b5fa9dcd 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -1328,6 +1328,12 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, return -EFBIG; } + if ((uint64_t)s->bat_size * s->tracks < bs->total_sectors) { + error_setg(errp, "Invalid image: Catalog size too small for " + "advertised disk size"); + return -EINVAL; + } + size = bat_entry_off(s->bat_size); s->header_size = ROUND_UP(size, bdrv_opt_mem_align(bs->file->bs)); s->header = qemu_try_blockalign(bs->file->bs, s->header_size); diff --git a/tests/qemu-iotests/tests/parallels-checks b/tests/qemu-iotests/tests/parallels-checks index abd119bc7b..d2a08049d9 100755 --- a/tests/qemu-iotests/tests/parallels-checks +++ b/tests/qemu-iotests/tests/parallels-checks @@ -225,6 +225,27 @@ echo "== an unallocated cluster still reads as zeroes ==" # Clear image _make_test_img $SIZE +echo "== TEST OVERSIZED VIRTUAL DISK CHECK ==" + +BAT_ENTRIES_OFFSET=32 +NB_SECTORS_OFFSET=36 + +TRACKS=$(peek_file_le $TEST_IMG $CLUSTER_SIZE_OFFSET 4) +BAT_ENTRIES=$(peek_file_le $TEST_IMG $BAT_ENTRIES_OFFSET 4) +COVERED_SECTORS=$((BAT_ENTRIES * TRACKS)) + +echo "== advertise one more cluster than the BAT covers ==" +poke_file_le "$TEST_IMG" $NB_SECTORS_OFFSET 8 $((COVERED_SECTORS + TRACKS)) + +echo "== open must fail cleanly instead of aborting ==" +_img_info + +echo "== write into the uncovered range must fail cleanly too ==" +{ $QEMU_IO -c "write -P 0x41 $((COVERED_SECTORS * 512)) $CLUSTER_SIZE" "$TEST_IMG"; } 2>&1 | _filter_qemu_io | _filter_testdir + +# Clear image +_make_test_img $SIZE + echo "== TEST BAT ENTRY POINTING OUTSIDE IMAGE ==" echo "== corrupt image: point first cluster far outside the file ==" diff --git a/tests/qemu-iotests/tests/parallels-checks.out b/tests/qemu-iotests/tests/parallels-checks.out index 914616d023..c33f3852a8 100644 --- a/tests/qemu-iotests/tests/parallels-checks.out +++ b/tests/qemu-iotests/tests/parallels-checks.out @@ -141,6 +141,13 @@ virtual size: 4 MiB (4194304 bytes) read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +== TEST OVERSIZED VIRTUAL DISK CHECK == +== advertise one more cluster than the BAT covers == +== open must fail cleanly instead of aborting == +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Invalid image: Catalog size too small for advertised disk size +== write into the uncovered range must fail cleanly too == +qemu-io: can't open device TEST_DIR/t.parallels: Invalid image: Catalog size too small for advertised disk size +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 == TEST BAT ENTRY POINTING OUTSIDE IMAGE == == corrupt image: point first cluster far outside the file == == read-only read must return zeroes, not an I/O error == From 352587cbe2d4cde1cece16e2f6a9e1e4190a03e6 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 22 Jul 2026 18:30:05 +0200 Subject: [PATCH 228/860] MAINTAINERS: update parallels tree location src.openvz.org is become unmaintained, point to the GitLab tree instead. Signed-off-by: Denis V. Lunev CC: Stefan Hajnoczi --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a28935c898..902db77218 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4393,7 +4393,7 @@ F: block/parallels.c F: block/parallels-ext.c F: docs/interop/parallels.rst F: docs/interop/prl-xml.rst -T: git https://src.openvz.org/scm/~den/qemu.git parallels +T: git https://gitlab.com/dlunev/qemu.git parallels qed M: Stefan Hajnoczi From 30724cfee678b08f1975741e1f3035b9306d114e Mon Sep 17 00:00:00 2001 From: Alexander Mikhalitsyn Date: Thu, 9 Jul 2026 10:47:28 +0200 Subject: [PATCH 229/860] hw/nvme: use GPtrArray for blocker_features Let's use GPtrArray to build a list of blocker features and then g_strjoinv() to build a final comma-delimited string. While previous approach was technically correct, it is fragile (because we need to take care of static buffer size choice) and Coverity dislikes it too. Note, that we use g_ptr_array_new() to allocate array which means that GDestroyNotify callback is not set, so we can pass pointers to a static memory like g_ptr_array_add(..., (gpointer) "SR-IOV") without any problems as there won't be any attempt to free that memory. Resolves: Coverity CID 1663673 Suggested-by: Peter Maydell Signed-off-by: Alexander Mikhalitsyn Reviewed-by: Peter Maydell Reviewed-by: Klaus Jensen [k.jensen: change cast from gpointer to void ptr] Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index a67e159889..086048b689 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -9352,22 +9352,11 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) } } -#define BLOCKER_FEATURES_MAX_LEN 256 - -static inline void nvme_add_blocker_feature(char *blocker_features, - const char *feature) -{ - if (strlen(blocker_features) > 0) { - g_strlcat(blocker_features, ", ", BLOCKER_FEATURES_MAX_LEN); - } - g_strlcat(blocker_features, feature, BLOCKER_FEATURES_MAX_LEN); -} - static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, Error **errp) { uint64_t unsupported_cap, cap = ldq_le_p(&n->bar.cap); - char blocker_features[BLOCKER_FEATURES_MAX_LEN] = ""; + g_autoptr(GPtrArray) blocker_features = g_ptr_array_new(); bool adm_cmd_security_checked = false; bool cmd_io_mgmt_checked = false; bool cmd_zone_checked = false; @@ -9416,15 +9405,15 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, } if (namespaces_num > 1) { - nvme_add_blocker_feature(blocker_features, - "Namespace Attachment"); + g_ptr_array_add(blocker_features, + (void *) "Namespace Attachment"); } break; } case NVME_ADM_CMD_VIRT_MNGMT: if (n->params.sriov_max_vfs) { - nvme_add_blocker_feature(blocker_features, "SR-IOV"); + g_ptr_array_add(blocker_features, (void *) "SR-IOV"); } break; @@ -9435,7 +9424,7 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, } if (pci_dev->spdm_port) { - nvme_add_blocker_feature(blocker_features, "SPDM"); + g_ptr_array_add(blocker_features, (void *) "SPDM"); } adm_cmd_security_checked = true; @@ -9469,7 +9458,7 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, /* check for NVME_IOMS_MO_RUH_UPDATE */ if (n->subsys->params.fdp.enabled) { - nvme_add_blocker_feature(blocker_features, "FDP"); + g_ptr_array_add(blocker_features, (void *) "FDP"); } cmd_io_mgmt_checked = true; @@ -9504,8 +9493,8 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, } if (ns->params.zoned) { - nvme_add_blocker_feature(blocker_features, - "Zoned Namespace"); + g_ptr_array_add(blocker_features, + (void *) "Zoned Namespace"); break; } } @@ -9525,24 +9514,28 @@ static bool nvme_set_migration_blockers(NvmeCtrl *n, PCIDevice *pci_dev, * covered by unsupported_cap check. */ if (NVME_CAP_CMBS(cap)) { - nvme_add_blocker_feature(blocker_features, "CMB"); + g_ptr_array_add(blocker_features, (void *) "CMB"); cap &= ~((uint64_t)CAP_CMBS_MASK << CAP_CMBS_SHIFT); } if (NVME_CAP_PMRS(cap)) { - nvme_add_blocker_feature(blocker_features, "PMR"); + g_ptr_array_add(blocker_features, (void *) "PMR"); cap &= ~((uint64_t)CAP_PMRS_MASK << CAP_PMRS_SHIFT); } unsupported_cap = cap & ~NVME_MIGRATION_SUPPORTED_CAP_BITS; if (unsupported_cap) { - nvme_add_blocker_feature(blocker_features, "unknown capability"); + g_ptr_array_add(blocker_features, (void *) "unknown capability"); } assert(n->migration_blocker == NULL); - if (strlen(blocker_features) > 0) { + if (blocker_features->len > 0) { + g_autofree char *blocker_list = NULL; + + g_ptr_array_add(blocker_features, NULL); + blocker_list = g_strjoinv(", ", (void *)blocker_features->pdata); error_setg(&n->migration_blocker, - "Migration is not supported for %s", blocker_features); + "Migration is not supported for %s", blocker_list); if (migrate_add_blocker(&n->migration_blocker, errp) < 0) { return false; } From 6e5ca34f3870a8cad7a61395f82d40df62cb5d52 Mon Sep 17 00:00:00 2001 From: Daniel Paziyski Date: Sun, 26 Jul 2026 20:30:08 +0200 Subject: [PATCH 230/860] hw/nvme: fix assertion failure on subregion removal When a controller is created with a MSI-X exclusive BAR, the bar0 memory region is not used at all, and so, the iomem region is not added as a subregion of it. However, when removing a NVMe controller, the iomem region is unconditionally removed as a subregion of bar0, causing an assertion failure. Remove the iomem memory region as a subregion of bar0 only if not using a MSI-X exclusive BAR. QEMU options (requires a hotunplug-aware OS): -M q35 -device pcie-root-port,id=rp0 \ -device nvme,serial=ctrl0,id=ctrl0,bus=rp0,msix-exclusive-bar=on In the QEMU monitor, or by causing an ejection from the OS: device_del ctrl0 Message in stderr: qemu-system-x86_64: ../system/memory.c:2617: memory_region_del_subregion: Assertion `subregion->container == mr' failed. Fixes: fa905f65c554 ("hw/nvme: add machine compatibility parameter to enable msix exclusive bar") Fixes: 9162f1012576 ("hw/nvme: fix msix_uninit with exclusive bar") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4090 Signed-off-by: Daniel Paziyski Reviewed-by: Klaus Jensen Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 086048b689..e1639b3839 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -9703,10 +9703,9 @@ static void nvme_exit(PCIDevice *pci_dev) msix_uninit_exclusive_bar(pci_dev); } else { msix_uninit(pci_dev, &n->bar0, &n->bar0); + memory_region_del_subregion(&n->bar0, &n->iomem); } - memory_region_del_subregion(&n->bar0, &n->iomem); - migrate_del_blocker(&n->migration_blocker); } From df67805ee9b15f4c7d54f0e6276d8f9889ec36c4 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Tue, 28 Jul 2026 12:37:57 +0200 Subject: [PATCH 231/860] hw/nvme: fix cross-namespace copy dif buffer overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NVMe specification allows a controller with multiple namespaces to use different LBA formats per namespace. One implication of this is that the destination namespace may have a metadata area for PI, but the source does not. In that case, the controller shall generate the protection information, but the bounce buffer is erroneously allocated without space for that, causing a buffer overflow. Fix the allocation. Cc: qemu-stable@nongnu.org Fixes: d522aef88d42 ("hw/nvme: add cross namespace copy support") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3387 Reported-by: Jihe Wang Reported-by: boy juju Reported-by: contact Reported-by: david korczynski Reported-by: Brian Chastain (off_by_one / Curious-Keeper) Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e1639b3839..acd10fcc67 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -3210,7 +3210,7 @@ static void nvme_do_copy(NvmeCopyAIOCB *iocb) uint16_t prinfow = ((copy->control[2] >> 2) & 0xf); uint64_t slba; uint32_t nlb; - size_t len; + size_t len, blen; uint16_t status; uint32_t dnsid = le32_to_cpu(req->cmd.nsid); uint32_t snsid = dnsid; @@ -3331,10 +3331,13 @@ static void nvme_do_copy(NvmeCopyAIOCB *iocb) } g_free(iocb->bounce); - iocb->bounce = g_malloc_n(le16_to_cpu(sns->id_ns.mssrl), - sns->lbasz + sns->lbaf.ms); + assert(g_size_checked_mul(&blen, le16_to_cpu(sns->id_ns.mssrl), + sns->lbasz + MAX(sns->lbaf.ms, dns->lbaf.ms))); + + iocb->bounce = g_malloc(blen); qemu_iovec_reset(&iocb->iov); + assert(len <= blen); qemu_iovec_add(&iocb->iov, iocb->bounce, len); block_acct_start(blk_get_stats(sns->blkconf.blk), &iocb->acct.read, 0, From c39e05af439a3a2bd58a847fc30d7eb590fcf61e Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Tue, 28 Jul 2026 11:42:29 +0200 Subject: [PATCH 232/860] hw/nvme: fix unintentional integer overflow in shift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix potentially overflowing shift operation. Cc: qemu-stable@nongnu.org Suggested-by: Peter Maydell Suggested-by: Philippe Mathieu-Daudé Resolves: Coverity CID 1663674 Fixes: ec917cd49918 ("hw/nvme: fix FDP set FDP events") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index acd10fcc67..bd6ad64b20 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -196,6 +196,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/log.h" @@ -6625,7 +6626,8 @@ static uint16_t nvme_set_feature_fdp_events(NvmeCtrl *n, NvmeNamespace *ns, if (!shift && event_type) { continue; } - event_mask |= (1 << nvme_fdp_evf_shifts[events[i]]); + event_mask = + deposit64(event_mask, nvme_fdp_evf_shifts[events[i]], 1, 1); } if (enable) { From e1705a25aff35635c360bbaba4c2731d019a422a Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 29 Jul 2026 06:16:08 -0400 Subject: [PATCH 233/860] Update version for v11.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 f9e860890c..5399c18e4a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.0.91 +11.0.92 From 9e3df3019d21c54df435406fca31b3e145b29702 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 28 Jul 2026 21:20:37 +0200 Subject: [PATCH 234/860] linux-user: Fix msqid_ds struct wrt 32-bit big endian architectures Make sure that the time entries (msg_stime, msg_rtime and msg_ctime) are defined as 64-bit time_t values, since the userspace may access the whole 64-bit value. By this change we fix the word ordering for 32-bit big endian architectures as well. This fixes the msgctl01 LTP testcase on hppa32. Cc: qemu-stable@nongnu.org Signed-off-by: Helge Deller --- linux-user/syscall.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 740142825d..c93b770ced 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4216,21 +4216,15 @@ static inline abi_long do_semtimedop(int semid, } #endif +#define target_time64_t abi_ullong +#define target_swap_time64(x) tswap64(x) + struct target_msqid_ds { struct target_ipc_perm msg_perm; - abi_ulong msg_stime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused1; -#endif - abi_ulong msg_rtime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused2; -#endif - abi_ulong msg_ctime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused3; -#endif + target_time64_t msg_stime; + target_time64_t msg_rtime; + target_time64_t msg_ctime; abi_ulong __msg_cbytes; abi_ulong msg_qnum; abi_ulong msg_qbytes; @@ -4249,9 +4243,9 @@ static inline abi_long target_to_host_msqid_ds(struct msqid_ds *host_md, return -TARGET_EFAULT; if (target_to_host_ipc_perm(&(host_md->msg_perm),target_addr)) return -TARGET_EFAULT; - host_md->msg_stime = tswapal(target_md->msg_stime); - host_md->msg_rtime = tswapal(target_md->msg_rtime); - host_md->msg_ctime = tswapal(target_md->msg_ctime); + host_md->msg_stime = target_swap_time64(target_md->msg_stime); + host_md->msg_rtime = target_swap_time64(target_md->msg_rtime); + host_md->msg_ctime = target_swap_time64(target_md->msg_ctime); host_md->__msg_cbytes = tswapal(target_md->__msg_cbytes); host_md->msg_qnum = tswapal(target_md->msg_qnum); host_md->msg_qbytes = tswapal(target_md->msg_qbytes); @@ -4270,9 +4264,9 @@ static inline abi_long host_to_target_msqid_ds(abi_ulong target_addr, return -TARGET_EFAULT; if (host_to_target_ipc_perm(target_addr,&(host_md->msg_perm))) return -TARGET_EFAULT; - target_md->msg_stime = tswapal(host_md->msg_stime); - target_md->msg_rtime = tswapal(host_md->msg_rtime); - target_md->msg_ctime = tswapal(host_md->msg_ctime); + target_md->msg_stime = target_swap_time64(host_md->msg_stime); + target_md->msg_rtime = target_swap_time64(host_md->msg_rtime); + target_md->msg_ctime = target_swap_time64(host_md->msg_ctime); target_md->__msg_cbytes = tswapal(host_md->__msg_cbytes); target_md->msg_qnum = tswapal(host_md->msg_qnum); target_md->msg_qbytes = tswapal(host_md->msg_qbytes); From 518a4aa01b88ff814dbf8668016f3ec910426283 Mon Sep 17 00:00:00 2001 From: no92 Date: Fri, 24 Jul 2026 18:01:04 +0200 Subject: [PATCH 235/860] linux-user: fix incorrect msg_l[sr]pid members of target_msqid_ds The members are declared as __kernel_pid_t in Linux UAPI headers. Analogous members in struct target_shmid_ds (shm_[cl]pid) are also declared as abi_int. Cc: qemu-stable@nongnu.org Fixes: 1c54ff97bbde ("linux-user: fix and cleanup IPCOP_msg* ipc calls handling") Signed-off-by: no92 Reviewed-by: Pierrick Bouvier Signed-off-by: Helge Deller --- linux-user/syscall.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c93b770ced..547784d971 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4228,8 +4228,8 @@ struct target_msqid_ds abi_ulong __msg_cbytes; abi_ulong msg_qnum; abi_ulong msg_qbytes; - abi_ulong msg_lspid; - abi_ulong msg_lrpid; + abi_int msg_lspid; + abi_int msg_lrpid; abi_ulong __unused4; abi_ulong __unused5; }; @@ -4249,8 +4249,8 @@ static inline abi_long target_to_host_msqid_ds(struct msqid_ds *host_md, host_md->__msg_cbytes = tswapal(target_md->__msg_cbytes); host_md->msg_qnum = tswapal(target_md->msg_qnum); host_md->msg_qbytes = tswapal(target_md->msg_qbytes); - host_md->msg_lspid = tswapal(target_md->msg_lspid); - host_md->msg_lrpid = tswapal(target_md->msg_lrpid); + host_md->msg_lspid = tswap32(target_md->msg_lspid); + host_md->msg_lrpid = tswap32(target_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 0); return 0; } @@ -4270,8 +4270,8 @@ static inline abi_long host_to_target_msqid_ds(abi_ulong target_addr, target_md->__msg_cbytes = tswapal(host_md->__msg_cbytes); target_md->msg_qnum = tswapal(host_md->msg_qnum); target_md->msg_qbytes = tswapal(host_md->msg_qbytes); - target_md->msg_lspid = tswapal(host_md->msg_lspid); - target_md->msg_lrpid = tswapal(host_md->msg_lrpid); + target_md->msg_lspid = tswap32(host_md->msg_lspid); + target_md->msg_lrpid = tswap32(host_md->msg_lrpid); unlock_user_struct(target_md, target_addr, 1); return 0; } From 8d0ac5933aa3476283c14680f165727268103fcd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 29 Jul 2026 12:17:10 -0700 Subject: [PATCH 236/860] linux-user/aarch64: Fix SHADOW_STACK_SET_TOKEN The token is not computed via TARGET_PAGE_SIZE, but via a fixed 12-bit field. Cc: qemu-stable@nongnu.org Fixes: ad1afe433fa ("linux-user/aarch64: Implement map_shadow_stack syscall") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4106 Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Helge Deller --- linux-user/syscall.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 547784d971..dc028686f4 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6900,7 +6900,8 @@ static abi_long do_map_shadow_stack(CPUArchState *env, abi_ulong addr, /* Leave an extra empty frame at top-of-stack. */ cap_ptr -= 8; } - cap_val = (cap_ptr & TARGET_PAGE_MASK) | 1; + /* Note the 12 bit field is unaffected by current page size. */ + cap_val = deposit64(cap_ptr, 0, 12, 1); if (put_user_u64(cap_val, cap_ptr)) { /* Allocation succeeded above. */ g_assert_not_reached(); From 702252a0dbc3ad9a51e4092a986b2f98e8c6adcb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 31 Jul 2026 11:24:27 +0100 Subject: [PATCH 237/860] target/arm: Fix b16b16 feature test for SME2 BFCLAMP, BFMAX, BFMIN These are controlled by FEAT_SVE_B16B16 not FEAT_SME_B16B16. Cc: qemu-stable@nongnu.org Fixes: bc65d2bd1cb ("target/arm: Implement SME2 Multiple and Single SVE Destructive") Fixes: 930760eb753 ("target/arm: Implement SME2 Multiple Vectors SVE Destructive") Fixes: 8b61eff8e72 ("target/arm: Implement SME2 FCLAMP, SCLAMP, UCLAMP") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sme.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index dcc4690fba..0794794e3d 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -756,7 +756,7 @@ static bool do_z2z_n1_fpst(DisasContext *s, arg_z2z_en *a, return false; } /* These insns use MO_8 to encode BFloat16. */ - if (esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + if (esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { return false; } if (!sme_sm_enabled_check(s)) { @@ -793,7 +793,7 @@ static bool do_z2z_nn_fpst(DisasContext *s, arg_z2z_en *a, if (fn == NULL) { return false; } - if (esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + if (esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { return false; } if (!sme_sm_enabled_check(s)) { @@ -1850,7 +1850,7 @@ static bool trans_FCLAMP(DisasContext *s, arg_zzz_en *a) return false; } /* This insn uses MO_8 to encode BFloat16. */ - if (a->esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { return false; } if (!sme_sm_enabled_check(s)) { From dd0d695306de823c1a782fecdf9744f618afbacb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 31 Jul 2026 11:24:27 +0100 Subject: [PATCH 238/860] target/arm: Don't NOP the SEV insn on v6K CPUs When we implememented SEV to do something on A-profile rather than being a nop, we got the condition slightly wrong, and made it only effective from v7. In fact the instruction's Arm encoding has non-NOP behaviour from ARMv6K. The effect is that a kernel boot may hang on a v6K CPU like the ARM11MPCore. (This wouldn't have been so noticeable if we feature checked the WFE instruction, and had made the same mistake for the condition on both instructions. But we never have done the feature checks that we ought on WFE, so the mistake on SEV meant that we showed the 11mpcore guest a WFE that did something and a SEV that was a NOP.) The v7A Arm ARM is not entirely clear about whether v6K has the Thumb SEV encoding or not: it says "ARMv7 (executes as NOP in ARMv6T2)", leaving v6K not stated. The 11MPCore TRM says it has at least WFI in both Arm and Thumb, and the v7A Arm ARM uses the same condition text for WFI, so I make the assumption that WFI, WFE, and SEV all get their functionality for both Thumb and Arm in v6K. It's possible that this differed between v6K CPUs -- the 1176 TRM says it has the v6K STREXD/STREXH/STREXB etc, but the WFI is the old-style cp15 one. Keeping the condition check the same for both Thumb and Arm encodings is the conservative choice: if guests try to execute the Thumb SEV insn it will be because they want SEV, not because they want a NOP. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4044 Fixes: 60e7ee5bb7cd ("target/arm: implements SEV/SEVL for all modes") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260728111629.1705308-2-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index a1fc050618..6e52ad082e 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -3246,12 +3246,19 @@ static bool trans_YIELD(DisasContext *s, arg_YIELD *a) static bool trans_SEV(DisasContext *s, arg_SEV *a) { /* - * SEV is a NOP for user-mode emulation. For v6T2 and earlier - * non-M-profile cores this encoding is a NOP hint. + * SEV is a NOP for user-mode emulation. The instruction is + * also a NOP hint on cores that pre-date the architectural + * feature that adds it: + * - M-profile always has SEV + * - for A/R profile, it exists from v6K onward + * The v7A Arm ARM is not entirely clear about whether v6K has the + * Thumb SEV or not; we make the condition the same, to be + * conservative. (If guests try to execute the Thumb SEV insn it + * will be because they want SEV, not because they want a NOP.) */ #ifndef CONFIG_USER_ONLY if (arm_dc_feature(s, ARM_FEATURE_M) || - arm_dc_feature(s, ARM_FEATURE_V7)) { + arm_dc_feature(s, ARM_FEATURE_V6K)) { gen_helper_sev(tcg_env); } #endif From d88781a4e8881bae032802f2b5440274ceece15a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 31 Jul 2026 11:24:27 +0100 Subject: [PATCH 239/860] target/arm: Make YIELD, WFI and WFE be NOPs on pre-v6K The YIELD, WFI and WFE instructions are in the NOP hint space, and were only defined to actual non-NOP instructions starting in the v6K architecture; they are also present for all M-profile architecture versions. We never did check the architecture version before making these instructions have their special behaviour. Mostly this has not been a problem because a guest won't execute one of these insns unless it is prepared for it to have its usual effect, and because we implemented SEV and WFE as NOPs anyway. Now we have implemented SEV and WFE to be more than just NOPs, it's important that we have the same condition on the SEV as the WFE, so that we either NOP both or else implement both. A guest probably won't try to use SEV/WFE on CPUs that don't implement them, but it is valid for it to do that and rely on them both being NOPs (and so a WFE-loop falls back to a pure busy-wait loop). Add the "only if M profile or v6K or better" check to YIELD, WFE and WFI. This means that all the insns in the NOP-hint space for A32, T32 and T16 have a correct feature check. Fixes: 60e7ee5bb7cd ("target/arm: implements SEV/SEVL for all modes") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260728111629.1705308-3-peter.maydell@linaro.org --- target/arm/tcg/translate.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 6e52ad082e..055f3c5b40 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -3235,10 +3235,14 @@ static bool trans_YIELD(DisasContext *s, arg_YIELD *a) * the next round-robin scheduled vCPU gets a crack. When running in * MTTCG we don't generate jumps to the helper as it won't affect the * scheduling of other vCPUs. + * This is a NOP hint on older architectures. */ - if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_YIELD; + if (arm_dc_feature(s, ARM_FEATURE_M) || + arm_dc_feature(s, ARM_FEATURE_V6K)) { + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_YIELD; + } } return true; } @@ -3280,17 +3284,30 @@ static bool trans_SEVL(DisasContext *s, arg_SEV *a) static bool trans_WFE(DisasContext *s, arg_WFE *a) { - /* For WFE, halt the vCPU until an event. */ - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_WFE; + /* + * For WFE, halt the vCPU until an event. This is a NOP + * hint on older architectures, with the same conditions + * as SEV. + */ + if (arm_dc_feature(s, ARM_FEATURE_M) || + arm_dc_feature(s, ARM_FEATURE_V6K)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_WFE; + } return true; } static bool trans_WFI(DisasContext *s, arg_WFI *a) { - /* For WFI, halt the vCPU until an IRQ. */ - gen_update_pc(s, curr_insn_len(s)); - s->base.is_jmp = DISAS_WFI; + /* + * For WFI, halt the vCPU until an IRQ. This is a NOP + * hint on older architectures. + */ + if (arm_dc_feature(s, ARM_FEATURE_M) || + arm_dc_feature(s, ARM_FEATURE_V6K)) { + gen_update_pc(s, curr_insn_len(s)); + s->base.is_jmp = DISAS_WFI; + } return true; } From c490037c1349f503d7986391b24db47362a204eb Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 31 Jul 2026 11:24:27 +0100 Subject: [PATCH 240/860] target/arm: Remove stale comment about WFE/SEV implementation We forgot to remove a comment about WFE/SEV only being implemented for M-profile when we added the A-profile support for this; delete the stale text. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260728111629.1705308-4-peter.maydell@linaro.org --- target/arm/cpu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 03a30afcbe..c0492c8dfb 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -766,7 +766,6 @@ typedef struct CPUArchState { /* * The event register is shared by all ARM profiles (A/R/M), * so it is stored in the top-level CPU state. - * WFE/SEV handling is currently implemented only for M-profile. */ bool event_register; From c25f69595ad052473463540f4d7944b9b73e94ec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 8 Jun 2026 17:15:21 -0700 Subject: [PATCH 241/860] target/i386: Update cc_op for SAHF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing the call to gen_compute_eflags meant we no longer updated cc_op after computing EFLAGS. Cc: qemu-stable@nongnu.org Fixes: da7649c6aeef ("target/i386/tcg: do not compute all flags for SAHF") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3537 Tested-by: Christian Quante Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/emit.c.inc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index e4ef5a8758..473f415766 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3780,10 +3780,13 @@ static void gen_SAHF(DisasContext *s, X86DecodedInsn *decode) return gen_illegal_opcode(s); } tcg_gen_shri_tl(s->T0, cpu_regs[R_EAX], 8); - gen_neg_setcc(s, JCC_O << 1, cpu_cc_src); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); + gen_neg_setcc(s, JCC_O << 1, s->T1); + tcg_gen_andi_tl(s->T1, s->T1, CC_O); tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); + tcg_gen_or_tl(s->T0, s->T0, s->T1); + + decode->cc_src = s->T0; + decode->cc_op = CC_OP_EFLAGS; } static void gen_SALC(DisasContext *s, X86DecodedInsn *decode) From f498c21b3595157d6fff8fb4f546cfecb14bd6dc Mon Sep 17 00:00:00 2001 From: "Tycho Andersen (AMD)" Date: Mon, 27 Jul 2026 09:00:38 -0600 Subject: [PATCH 242/860] hw/vfio: Fix liveness check in vfio_connect_kvm_msi_virq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While working on savevm/loadvm for a new vfio device, I encountered the crash below. Since vfio_connect_kvm_msi_virq() didn't check the ->use flag for the vector, it would pass an unused vector down to vfio_cpr_load_vector_fd() which would crash. Fix this by checking the ->use flag along with the virq number to detect whether a vector is valid or not. Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault. 0x0000555555a891ef in vfio_cpr_load_vector_fd (vdev=vdev@entry=0x0, name=name@entry=0x555555eeb27e "kvm_interrupt", nr=nr@entry=1) at ../hw/vfio/cpr.c:44 44 g_autofree char *fdname = STRDUP_VECTOR_FD_NAME(vdev, name); (gdb) bt #0 0x0000555555a891ef in vfio_cpr_load_vector_fd (vdev=vdev@entry=0x0, name=name@entry=0x555555eeb27e "kvm_interrupt", nr=nr@entry=1) at ../hw/vfio/cpr.c:44 #1 0x0000555555ce64a1 in vfio_notifier_init (vdev=0x0, e=e@entry=0x5555586971b4, name=name@entry=0x555555eeb27e "kvm_interrupt", nr=nr@entry=1, errp=errp@entry=0x0) at ../hw/vfio/pci.c:79 #2 0x0000555555ce721e in vfio_connect_kvm_msi_virq (vector=0x5555586971a8, nr=nr@entry=1) at ../hw/vfio/pci.c:601 #3 0x0000555555cea5a5 in vfio_connect_kvm_msi_virq (nr=1, vector=) at ../hw/vfio/pci.c:597 #4 vfio_pci_commit_kvm_msi_virq_batch (vdev=0x55555906de40) at ../hw/vfio/pci.c:822 #5 0x0000555555cea9f2 in vfio_msix_enable (vdev=vdev@entry=0x55555906de40) at ../hw/vfio/pci.c:850 #6 0x0000555555ceb152 in vfio_pci_load_config (vbasedev=0x55555906e900, f=) at ../hw/vfio/pci.c:3088 #7 0x0000555555a8c765 in vfio_load_device_config_state (f=0x5555574a43d0, opaque=0x55555906e900) at ../hw/vfio/migration.c:278 #8 0x0000555555b3a522 in vmstate_load (f=f@entry=0x5555574a43d0, se=se@entry=0x5555591edd40, errp=errp@entry=0x7fffffffe130) at ../migration/savevm.c:971 #9 0x0000555555b3ab1a in qemu_loadvm_section_start_full (f=f@entry=0x5555574a43d0, type=type@entry=4 '\004', errp=errp@entry=0x7fffffffe130) at ../migration/savevm.c:2654 #10 0x0000555555b3e1ee in qemu_loadvm_state_main (f=f@entry=0x5555574a43d0, mis=mis@entry=0x5555571de5a0, errp=0x7fffffffe130, errp@entry=0x555557157c10 ) at ../migration/savevm.c:2973 #11 0x0000555555b3f7b7 in qemu_loadvm_state (f=f@entry=0x5555574a43d0, errp=errp@entry=0x555557157c10 ) at ../migration/savevm.c:3058 #12 0x0000555555b40863 in load_snapshot (name=0x7fffffffecc9 "foo", vmstate=vmstate@entry=0x0, has_devices=has_devices@entry=false, devices=devices@entry=0x0, errp=errp@entry=0x555557157c10 ) at ../migration/savevm.c:3452 #13 0x0000555555adc211 in qmp_x_exit_preconfig (errp=0x555557157c10 ) at ../system/vl.c:2817 #14 qmp_x_exit_preconfig (errp=0x555557157c10 ) at ../system/vl.c:2802 #15 0x0000555555adf8ed in qemu_init (argc=, argv=) at ../system/vl.c:3849 #16 0x00005555558903fd in main (argc=, argv=) at ../system/main.c:71 Fixes: 30edcb4d4e7a ("vfio-pci: preserve MSI") Signed-off-by: Tycho Andersen (AMD) Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260727150038.2684512-1-tycho@kernel.org Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 380dd8c15f..b5280c3d2a 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -589,7 +589,7 @@ static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) { const char *name = "kvm_interrupt"; - if (vector->virq < 0) { + if (!vector->use || vector->virq < 0) { return; } From 338d81b22b4dc6c45299da1114925a4b086d6e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 21 Jul 2026 12:50:26 +0200 Subject: [PATCH 243/860] vfio/pci: Guard accel_irqchip_begin_route_changes() calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 49b2dcbd2422 ("accel/accel-irq: add generic begin_route_changes"), accel_irqchip_begin_route_changes() aborts when no accelerator irqchip is available. This causes a fatal error when running VFIO passthrough devices under TCG emulation: qemu-system-aarch64: can't initiate route change, no accel irqchip available The previous kvm_irqchip_begin_route_changes() was a simple inline that did not have a fatal path. The VFIO code already handles the absence of KVM MSI routing gracefully by falling back to userspace handling, but the new generic function aborts before that fallback can take effect. Guard the call sites in hw/vfio/pci.c with accel_msi_via_irqfd_enabled() so that route changes are only initiated when an accelerator irqchip is actually present. Fixes: 49b2dcbd2422 ("accel/accel-irq: add generic begin_route_changes") Cc: Magnus Kulke Link: https://lore.kernel.org/qemu-devel/20260721105026.3932297-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b5280c3d2a..428ab2f069 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -699,7 +699,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, if (msg) { if (vdev->defer_kvm_irq_routing) { vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); - } else { + } else if (accel_msi_via_irqfd_enabled()) { vfio_route_change = accel_irqchip_begin_route_changes(); vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); accel_irqchip_commit_route_changes(&vfio_route_change); @@ -801,7 +801,9 @@ void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev) { assert(!vdev->defer_kvm_irq_routing); vdev->defer_kvm_irq_routing = true; - vfio_route_change = accel_irqchip_begin_route_changes(); + if (accel_msi_via_irqfd_enabled()) { + vfio_route_change = accel_irqchip_begin_route_changes(); + } } void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) From 0d3db94a886610c5923e52cc23f841ccb9cb546c Mon Sep 17 00:00:00 2001 From: malike Date: Mon, 13 Jul 2026 11:17:50 +0800 Subject: [PATCH 244/860] block/cloop: fix integer overflow in total_sectors calculation The total_sectors is computed as n_blocks * sectors_per_block where both operands are uint32_t. The multiplication is performed in 32-bit arithmetic and can overflow when the product exceeds UINT32_MAX, producing a value much smaller than the true image size. The result is assigned to int64_t total_sectors but the 32-bit multiplication has already wrapped around, and the zero-extension to 64-bit does not recover the correct value. This causes the block layer to reject valid I/O requests (DoS) when the reported total_sectors is smaller than the actual image. Use 64-bit arithmetic by casting one operand to uint64_t so the multiplication is performed in 64-bit precision. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3972 Signed-off-by: Ma Like Message-ID: <20260713031750.58448-1-malike@kylinos.cn> Signed-off-by: Kevin Wolf --- block/cloop.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/block/cloop.c b/block/cloop.c index 443af1444e..a16f08e6ef 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -202,7 +202,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, s->current_block = s->n_blocks; s->sectors_per_block = s->block_size/512; - bs->total_sectors = s->n_blocks * s->sectors_per_block; + /* Cast to uint64_t to prevent uint32_t overflow */ + bs->total_sectors = (uint64_t)s->n_blocks * s->sectors_per_block; qemu_co_mutex_init(&s->lock); return 0; From 5b0ba385a024ad92c63c175317cd9374de2e6165 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 24 Jul 2026 13:13:09 +0200 Subject: [PATCH 245/860] block/accounting: take stats->lock in latency histogram setters block_latency_histogram_set() and block_latency_histograms_clear() replace BlockLatencyHistogram's nbins/boundaries/bins without taking stats->lock, while block_account_one_io() reads those same fields under that lock from whatever iothread completes the I/O. The result is usual use-after-free and qemu crash. Take stats->lock in both setters, matching the lock already held by the reader. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz CC: Vladimir Sementsov-Ogievskiy CC: Andrey Drobyshev Message-ID: <20260724111311.4086859-2-den@openvz.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/accounting.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/block/accounting.c b/block/accounting.c index f00fe99740..6e06c7609e 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -194,6 +194,8 @@ int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, return -EINVAL; } + qemu_mutex_lock(&stats->lock); + hist->nbins = new_nbins; g_free(hist->boundaries); hist->boundaries = g_new(uint64_t, hist->nbins - 1); @@ -206,6 +208,8 @@ int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, g_free(hist->bins); hist->bins = g_new0(uint64_t, hist->nbins); + qemu_mutex_unlock(&stats->lock); + return 0; } @@ -213,12 +217,16 @@ void block_latency_histograms_clear(BlockAcctStats *stats) { int i; + qemu_mutex_lock(&stats->lock); + for (i = 0; i < BLOCK_MAX_IOTYPE; i++) { BlockLatencyHistogram *hist = &stats->latency_histogram[i]; g_free(hist->bins); g_free(hist->boundaries); memset(hist, 0, sizeof(*hist)); } + + qemu_mutex_unlock(&stats->lock); } static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie, From 16f94ef4c6ce3e7ca6b02b796580e2cfc41e9789 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 24 Jul 2026 13:13:10 +0200 Subject: [PATCH 246/860] block/qapi: take stats->lock when reading BlockAcctStats for query-blockstats bdrv_query_blk_stats() reads BlockAcctStats's counters, latency histogram, and per-interval TimedAverage stats without stats->lock, while block_account_one_io() updates the same fields under that lock from an iothread. timed_average_min()/max()/avg() make this worse than a stale read: they call check_expirations(), which can reset a window's sum/count/min/max -- a write, not just a read -- so this is a genuine race with a concurrent writer, not merely a slower reader like the scalar counters. Take stats->lock for the whole call, both to close the race and to make the returned snapshot internally consistent (previously each field could reflect a different instant relative to concurrent updates). block_acct_queue_depth() used to take the lock itself on every call; since bdrv_query_blk_stats() is its only caller and now already holds the lock, that would self-deadlock. Make it require the caller to hold stats->lock instead (documented and asserted). Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz CC: Vladimir Sementsov-Ogievskiy CC: Andrey Drobyshev Message-ID: <20260724111311.4086859-3-den@openvz.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/accounting.c | 3 +-- block/qapi.c | 3 +++ include/block/accounting.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/block/accounting.c b/block/accounting.c index 6e06c7609e..038af37017 100644 --- a/block/accounting.c +++ b/block/accounting.c @@ -318,10 +318,9 @@ double block_acct_queue_depth(BlockAcctTimedStats *stats, uint64_t sum, elapsed; assert(type < BLOCK_MAX_IOTYPE); + assert(qemu_mutex_trylock(&stats->stats->lock) == -EBUSY); - qemu_mutex_lock(&stats->stats->lock); sum = timed_average_sum(&stats->latency[type], &elapsed); - qemu_mutex_unlock(&stats->stats->lock); return (double) sum / elapsed; } diff --git a/block/qapi.c b/block/qapi.c index eabfbfc258..1dfac51091 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -535,6 +535,8 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) BlockAcctTimedStats *ts = NULL; BlockLatencyHistogram *hgram; + qemu_mutex_lock(&stats->lock); + ds->rd_bytes = stats->nr_bytes[BLOCK_ACCT_READ]; ds->wr_bytes = stats->nr_bytes[BLOCK_ACCT_WRITE]; ds->zone_append_bytes = stats->nr_bytes[BLOCK_ACCT_ZONE_APPEND]; @@ -624,6 +626,7 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_ZONE_APPEND]); ds->flush_latency_histogram = bdrv_latency_histogram_stats(&hgram[BLOCK_ACCT_FLUSH]); + qemu_mutex_unlock(&stats->lock); } static BlockStats * GRAPH_RDLOCK diff --git a/include/block/accounting.h b/include/block/accounting.h index b1cf417b57..12d3246092 100644 --- a/include/block/accounting.h +++ b/include/block/accounting.h @@ -116,6 +116,7 @@ void block_acct_invalid(BlockAcctStats *stats, enum BlockAcctType type); void block_acct_merge_done(BlockAcctStats *stats, enum BlockAcctType type, int num_requests); int64_t block_acct_idle_time_ns(BlockAcctStats *stats); +/* Caller must hold stats->stats->lock. */ double block_acct_queue_depth(BlockAcctTimedStats *stats, enum BlockAcctType type); int block_latency_histogram_set(BlockAcctStats *stats, enum BlockAcctType type, From 842c89281a20c3ea783609c5ac9da226e017e490 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 24 Jul 2026 13:13:11 +0200 Subject: [PATCH 247/860] tests/unit: add reproducer for BlockAcctStats histogram locking race block_latency_histogram_set() and block_latency_histograms_clear() replace BlockLatencyHistogram's nbins/boundaries/bins without taking stats->lock, while block_account_one_io() reads those same fields under that lock from whatever iothread completes the I/O. Add a test that races two real threads against block_latency_histogram_set() and block_acct_start()/block_acct_done() on the same BlockAcctStats. Applied here it passes, since the previous two commits already take the lock; reverting them locally reproduces the abort this series fixes, in about a second. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz CC: Vladimir Sementsov-Ogievskiy CC: Andrey Drobyshev Message-ID: <20260724111311.4086859-4-den@openvz.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/unit/meson.build | 1 + tests/unit/test-block-accounting.c | 115 +++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 tests/unit/test-block-accounting.c diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 5ba6b1a230..dc3fb954c0 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -75,6 +75,7 @@ if have_block 'test-blockjob': [testblock], 'test-blockjob-txn': [testblock], 'test-block-backend': [testblock], + 'test-block-accounting': [testblock], 'test-block-iothread': [testblock], 'test-write-threshold': [testblock], 'test-crypto-hash': [crypto], diff --git a/tests/unit/test-block-accounting.c b/tests/unit/test-block-accounting.c new file mode 100644 index 0000000000..7aae491cfc --- /dev/null +++ b/tests/unit/test-block-accounting.c @@ -0,0 +1,115 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * BlockAcctStats latency histogram locking regression test + * + * Copyright (c) 2026 Virtuozzo International GmbH. + * + * Regression test for missing stats->lock in + * block_latency_histogram_set()/block_latency_histograms_clear(), + * racing block_account_one_io() reading the same fields from an + * iothread. Aborts reliably before the fix, passes after it. + */ + +#include "qemu/osdep.h" +#include "block/block.h" +#include "block/accounting.h" +#include "system/block-backend.h" +#include "system/block-backend-io.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "qemu/thread.h" + +#define RACE_DURATION_MS 2000 +#define NUM_READER_THREADS 8 + +static bool stop_workers; + +/* + * Different bin counts, so the writer's g_free()/g_new() churn can be + * caught mid-update. Values are small enough (nanoseconds) that plain + * back-to-back start/done calls exercise every bin without sleeping. + */ +static uint64List boundaries_a[] = { + { .next = &boundaries_a[1], .value = 1000 }, + { .next = &boundaries_a[2], .value = 5000 }, + { .next = NULL, .value = 50000 }, +}; + +static uint64List boundaries_b[] = { + { .next = &boundaries_b[1], .value = 800 }, + { .next = &boundaries_b[2], .value = 3000 }, + { .next = &boundaries_b[3], .value = 20000 }, + { .next = NULL, .value = 200000 }, +}; + +static void *writer_thread(void *opaque) +{ + BlockAcctStats *stats = opaque; + + while (!qatomic_read(&stop_workers)) { + block_latency_histogram_set(stats, BLOCK_ACCT_READ, boundaries_a); + block_latency_histogram_set(stats, BLOCK_ACCT_READ, boundaries_b); + block_latency_histograms_clear(stats); + } + + return NULL; +} + +static void *reader_thread(void *opaque) +{ + BlockAcctStats *stats = opaque; + + while (!qatomic_read(&stop_workers)) { + BlockAcctCookie cookie; + + block_acct_start(stats, &cookie, 4096, BLOCK_ACCT_READ); + block_acct_done(stats, &cookie); + } + + return NULL; +} + +static void test_latency_histogram_race(void) +{ + BlockBackend *blk = blk_new(qemu_get_aio_context(), + BLK_PERM_ALL, BLK_PERM_ALL); + BlockAcctStats *stats = blk_get_stats(blk); + QemuThread writer, readers[NUM_READER_THREADS]; + int i; + + /* Histogram has to be enabled (bins != NULL) before racing it. */ + g_assert(block_latency_histogram_set(stats, BLOCK_ACCT_READ, + boundaries_a) == 0); + + stop_workers = false; + qemu_thread_create(&writer, "hist-writer", writer_thread, stats, + QEMU_THREAD_JOINABLE); + for (i = 0; i < NUM_READER_THREADS; i++) { + qemu_thread_create(&readers[i], "hist-reader", reader_thread, stats, + QEMU_THREAD_JOINABLE); + } + + g_usleep(RACE_DURATION_MS * 1000); + qatomic_set(&stop_workers, true); + + qemu_thread_join(&writer); + for (i = 0; i < NUM_READER_THREADS; i++) { + qemu_thread_join(&readers[i]); + } + + blk_unref(blk); +} + +int main(int argc, char **argv) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/block-accounting/latency_histogram_race", + test_latency_histogram_race); + + return g_test_run(); +} From 0c43f801c0d7a31ef05bc22914cca0b0e28210a8 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 23 Jul 2026 10:45:17 -0400 Subject: [PATCH 248/860] dmg: fix out-of-bounds load in search_chunk() (CVE-2026-65929) The binary search in search_chunk() uses s->n_chunks as the (inclusive) upper bound. Chunk indices are in the right-open interval [0, s->n_chunks) so it is wrong to search all the way up to s->n_chunks rather than s->n_chunks - 1. The worst case security scenario I can see is convincing a victim to hotplug a malicious DMG file to a running guest, potentially causing QEMU to crash when loading from memory beyond the end of s->sectors[] or s->sectorscounts[]. This could be a denial of service. Fixes: CVE-2026-65929 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3844 Reported-by: boy juju Reported-by: Tristan Madani Signed-off-by: Stefan Hajnoczi Message-ID: <20260723144519.364701-2-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/dmg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/block/dmg.c b/block/dmg.c index 33dcb3a349..e325127d14 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -609,7 +609,10 @@ static inline int is_sector_in_chunk(BDRVDMGState *s, static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) { /* binary search */ - uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; + uint32_t chunk1 = 0, chunk2 = s->n_chunks - 1, chunk3; + if (s->n_chunks == 0) { + goto err; /* should never happen */ + } while (chunk1 <= chunk2) { chunk3 = (chunk1 + chunk2) / 2; if (s->sectors[chunk3] > sector_num) { From ab7b872f0c38ee2a576b93cf37b9945f622d48fb Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 23 Jul 2026 10:45:18 -0400 Subject: [PATCH 249/860] dmg: refuse to open files with no chunks The dmg block driver expects the disk image file to contain at least one chunk. Refuse to open such files. This ensures that dmg block driver state always has non-NULL s->sectors[] and related fields. Note that the previous commit fixed the only known way to trigger a crash. This patch is just for defense - let's avoid opening the file and having NULL pointers in dmg block driver state. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4021 Reported-by: Tristan Madani Signed-off-by: Stefan Hajnoczi Message-ID: <20260723144519.364701-3-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/dmg.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/block/dmg.c b/block/dmg.c index e325127d14..6f8120e033 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -559,6 +559,12 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + /* There must be at least one chunk */ + if (s->n_chunks == 0) { + ret = -EINVAL; + goto fail; + } + /* initialize zlib engine */ s->compressed_chunk = qemu_try_blockalign(bs->file->bs, ds.max_compressed_size + 1); From 370882d0869567e5f21b95229a763171e319d0db Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 23 Jul 2026 10:45:19 -0400 Subject: [PATCH 250/860] dmg: reject inconsistent UDRW chunk sector count and length (CVE-2026-65928) The chunk metadata contains both: - Sector count: number of 512-byte sectors in the virtual disk - Length: number of bytes in the image file The UDRW chunk type indicates uncompressed data that can be accessed directly. The code is missing input validation to verify that sector count is consistent with length. If sector count is larger than length, then read requests can access beyond the end of the s->uncompressed_chunk buffer. This is an out-of-bounds heap access that could lead to a crash or an information leak. While we're at it, also zero the end of the last sector when length is unaligned. This prevents information leaks from the s->uncompressed_chunk buffer. Fixes: CVE-2026-65928 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3846 Reported-by: boy juju Signed-off-by: Stefan Hajnoczi Message-ID: <20260723144519.364701-4-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/dmg.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/block/dmg.c b/block/dmg.c index 6f8120e033..5d7d3b8901 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -312,6 +312,21 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, goto fail; } + /* + * Uncompressed chunk length must match sector count. Compressed chunks + * are validated during dmg_read_chunk() since the uncompressed size is + * not known ahead of time. + */ + if (s->types[i] == UDRW) { + if (s->sectorcounts[i] != DIV_ROUND_UP(s->lengths[i], 512)) { + error_report("length %" PRIu64 " for chunk %" PRIu32 + " is inconsistent with sector count %" PRIu64, + s->lengths[i], i, s->sectorcounts[i]); + ret = -EINVAL; + goto fail; + } + } + update_max_chunk_size(s, i, &ds->max_compressed_size, &ds->max_sectors_per_chunk); offset += 40; @@ -722,6 +737,16 @@ dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) if (ret < 0) { return -1; } + + /* + * Zero the unread part of the last sector when chunk length is + * unaligned to avoid exposing uninitialized memory. Valid image + * files may never hit this case, but cover it to be safe. + */ + if (s->lengths[chunk] & 511) { + size_t trailing_bytes = 512 - (s->lengths[chunk] & 511); + memset(s->uncompressed_chunk + s->lengths[chunk], 0, trailing_bytes); + } break; case UDZE: /* zeros */ case UDIG: /* ignore */ From fe0f9e3ab2a8112dd9671bbb91365feba683f755 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 16 Jul 2026 17:35:52 +0200 Subject: [PATCH 251/860] qcow2: do not try to clear the dirty bit on a read-only node qcow2_do_close() -> qcow2_inactivate() clears the dirty bit with a plain write to bs->file, unconditionally. A read-only node can still be dirty, inherited from an earlier writable session, and that write then hits a missing BLK_PERM_WRITE and asserts in bdrv_co_write_req_prepare() (block/io.c) on an entirely ordinary close -- closing is expected, the dirty bit on a read-only node is not. Skip the clear for read-only nodes, same as read access already does. Any other still-dirty node keeps the unguarded write: it is expected to hold write permission, and a missing one there is a bug worth seeing. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz Message-ID: <20260716153552.3376009-1-den@openvz.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qcow2.c | 6 +++++- tests/qemu-iotests/039 | 11 +++++++++++ tests/qemu-iotests/039.out | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/block/qcow2.c b/block/qcow2.c index 19271b10a4..7292dd036c 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2870,7 +2870,11 @@ static int GRAPH_RDLOCK qcow2_inactivate(BlockDriverState *bs) strerror(-ret)); } - if (result == 0) { + /* + * A read-only node cannot resolve an inherited dirty bit here; + * leave it dirty, same as plain read access already does. + */ + if (result == 0 && !bdrv_is_read_only(bs)) { qcow2_mark_clean(bs); } diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index e43e7026ce..94a8bfe754 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -84,6 +84,17 @@ $QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io # The dirty bit must be set _qcow2_dump_header | grep incompatible_features +echo +echo "== Read-only open must not crash on close ==" + +# We must not try to write the QCOW2 header to a read-only image. +$QEMU_IMG info --image-opts \ + "driver=$IMGFMT,read-only=on,file.driver=file,file.filename=$TEST_IMG,file.read-only=off" \ + > /dev/null + +# The dirty bit must still be set: this open never wrote any guest data +_qcow2_dump_header | grep incompatible_features + echo echo "== Repairing the image file must succeed ==" diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index 8fdbcc528a..c66361128f 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -24,6 +24,9 @@ read 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) incompatible_features [0] +== Read-only open must not crash on close == +incompatible_features [0] + == Repairing the image file must succeed == ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure From 43e4791d99ab11c4bd12efaec061b3a43c8cde1c Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 15 Jul 2026 12:34:49 +0200 Subject: [PATCH 252/860] iotests: run the test pool with the 'fork' start method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit run_tests_pool() shares the runner via the class attribute TestRunner.shared_self, relying on worker processes to inherit it. That only works with the 'fork' start method. Python 3.14 switched the Linux default to 'forkserver', so workers see shared_self as None and parallel runs abort with: assert runner is not None AssertionError Only reproduces with Python 3.14+ and 'check -jN' (N > 1); meson runs one test per process and never calls run_tests_pool(), so CI is unaffected. Request get_context('fork') explicitly; it is available on all supported Python versions and a no-op before 3.14. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz Message-ID: <20260715103451.1930909-2-den@openvz.org> Reviewed-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/testrunner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index dbe2dddc32..cc36867719 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -26,7 +26,7 @@ import contextlib import json import shutil import sys -from multiprocessing import Pool +from multiprocessing import get_context from typing import List, Optional, Any, Sequence, Dict from testenv import TestEnv @@ -125,7 +125,7 @@ class TestRunner(contextlib.AbstractContextManager['TestRunner']): assert TestRunner.shared_self is None TestRunner.shared_self = self - with Pool(jobs) as p: + with get_context('fork').Pool(jobs) as p: results = p.starmap(self.proc_run_test, zip(tests, [test_field_width] * len(tests))) From 951227d37554b16a7d0155ad053f348408ddb876 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 15 Jul 2026 12:34:50 +0200 Subject: [PATCH 253/860] iotests: skip FUSE tests when FUSE is not usable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit file-io-error, fuse-allow-other and fuse-mmap-shared skip only when FUSE is not compiled in. When FUSE is built in but unusable at run time (no /dev/fuse access, fusermount lacking permissions), the export fails to mount with "Failed to mount FUSE session to export" and the tests report a spurious failure instead of skipping, like NBD tests already do for missing NBD support. Add _notrun_on_fuse_error() to common.rc and use it in the shell tests. fuse-mmap-shared is Python, so it gets an equivalent inline check. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz Message-ID: <20260715103451.1930909-3-den@openvz.org> Reviewed-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/common.rc | 14 ++++++++++++++ tests/qemu-iotests/tests/file-io-error | 4 +--- tests/qemu-iotests/tests/fuse-allow-other | 2 ++ tests/qemu-iotests/tests/fuse-mmap-shared | 9 ++++++--- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 298bc483e0..bcb1ec50a9 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -981,6 +981,20 @@ _require_drivers() done } +# Skip if FUSE is unusable: not compiled in, or the export failed to +# mount. $1 is the failing 'block-export-add' reply. +_notrun_on_fuse_error() +{ + case "$1" in + *"Parameter 'type' does not accept value 'fuse'"*) + _notrun "No FUSE support" + ;; + *"Failed to mount FUSE session"*) + _notrun "FUSE not usable in this environment" + ;; + esac +} + # Check that we have a file system that allows huge (but very sparse) files # _require_large_file() diff --git a/tests/qemu-iotests/tests/file-io-error b/tests/qemu-iotests/tests/file-io-error index fb8db73b31..0d970c102f 100755 --- a/tests/qemu-iotests/tests/file-io-error +++ b/tests/qemu-iotests/tests/file-io-error @@ -89,9 +89,7 @@ output=$(_send_qemu_cmd $QEMU_HANDLE \ 'return' \ | grep -v 'option allow_other only allowed if') -if echo "$output" | grep -q "Parameter 'type' does not accept value 'fuse'"; then - _notrun 'No FUSE support' -fi +_notrun_on_fuse_error "$output" echo "$output" echo diff --git a/tests/qemu-iotests/tests/fuse-allow-other b/tests/qemu-iotests/tests/fuse-allow-other index eaa39f8f23..50a36601d7 100755 --- a/tests/qemu-iotests/tests/fuse-allow-other +++ b/tests/qemu-iotests/tests/fuse-allow-other @@ -77,6 +77,8 @@ fuse_export_add() _notrun "allow_other not supported" fi + _notrun_on_fuse_error "$output" + echo "$output" } diff --git a/tests/qemu-iotests/tests/fuse-mmap-shared b/tests/qemu-iotests/tests/fuse-mmap-shared index 52941a3bb6..b190105894 100755 --- a/tests/qemu-iotests/tests/fuse-mmap-shared +++ b/tests/qemu-iotests/tests/fuse-mmap-shared @@ -28,9 +28,12 @@ def test_fuse_support(mount_point): }) test_qsd.stop() if 'error' in res: - assert (res['error']['desc'] == - "Parameter 'type' does not accept value 'fuse'") - iotests.notrun('No FUSE support') + desc = res['error']['desc'] + if desc == "Parameter 'type' does not accept value 'fuse'": + iotests.notrun('No FUSE support') + if 'Failed to mount FUSE session' in desc: + iotests.notrun('FUSE not usable in this environment') + assert False, desc # Shared mmap when using direct IO is only supported for Linux kernels >= 6.6 # with commit e78662e818f94 ("fuse: add a new fuse init flag to relax From 5add514ba36ed2cc2a5aa3be64c4adc72d4d9003 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 15 Jul 2026 12:34:51 +0200 Subject: [PATCH 254/860] iotests/migrate-bitmaps-postcopy-test: replace the timing assertion with a content check `downtime * 10 < postcopy_time` was an unnormalized wall-clock heuristic (commit e80a4150a5) that fails on fast hosts, where the bitmap payload now transfers in under a second. Check the actual invariant instead: right after RESUME, bitmap0's content hash on the destination must not yet match the fully migrated value. Throttle max-bandwidth first, since all-zero chunks skip the payload write and would otherwise let a fast host finish the transfer before the check runs. Signed-off-by: Denis V. Lunev CC: Kevin Wolf CC: Hanna Reitz CC: Vladimir Sementsov-Ogievskiy Message-ID: <20260715103451.1930909-4-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- .../tests/migrate-bitmaps-postcopy-test | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test index c519e6db8c..33ff2b861f 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-postcopy-test @@ -160,12 +160,26 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): self.vm_b.cmd('migrate-set-capabilities', capabilities=caps) + # Throttle so the chunks covering our discards (the only ones + # not skipped by the all-zero fast path) can't outrun the check + # below. + self.vm_a.cmd('migrate-set-parameters', max_bandwidth=16536) + self.vm_a.cmd('migrate', uri='exec:cat>' + fifo) self.vm_a.cmd('migrate-start-postcopy') event_resume = self.vm_b.event_wait('RESUME') self.vm_b_events.append(event_resume) + + # bitmap0 can't already have its final content: that requires + # the bit data to have actually arrived. + result = self.vm_b.qmp('x-debug-block-dirty-bitmap-sha256', + node='drive0', name='bitmap0') + assert result['return']['sha256'] != all_discards_sha256 + + self.vm_a.cmd('migrate-set-parameters', max_bandwidth=0) + return (event_resume, discards1_sha256, all_discards_sha256) def test_postcopy_success(self): @@ -186,7 +200,6 @@ class TestDirtyBitmapPostcopyMigration(iotests.QMPTestCase): downtime = event_dist(event_stop, event_resume) postcopy_time = event_dist(event_resume, event_complete) - assert downtime * 10 < postcopy_time if debug: print('downtime:', downtime) print('postcopy_time:', postcopy_time) From e554413bd2b4d6e1f31eebfb499bce5238b91646 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Wed, 10 Jun 2026 13:58:50 +0200 Subject: [PATCH 255/860] coroutine: fix lost wakeup in qemu_co_sleep_wake() cache_clean_timer_del_and_wait() cancels the cache-cleaner coroutine by setting s->cache_clean_interval = 0 and calling qemu_co_sleep_wake() to cut short its qemu_co_sleep_ns_wakeable(). qemu_co_sleep_wake() is fire-and-forget: it reads w->to_wake and silently returns when it is NULL. A sleeper that is between two iterations -- has just released s->lock but has not yet set w->to_wake inside qemu_co_sleep() -- loses the wake: iothread0 timer coroutine main thread (qcow2 close) ------------------------- ------------------------- while-body (holding s->lock): read interval = 600 wait_ns = 600 * NS release s->lock take s->lock interval = 0 qemu_co_sleep_wake(w): w->to_wake == NULL -> skip return qemu_co_queue_wait(exit, s->lock): release s->lock yield qemu_co_sleep_ns_wakeable: aio_timer_init(+600 s) qemu_co_sleep: cas scheduled NULL -> "qsns" w->to_wake = co yield [sleeps 600 s] cache_clean_timer_del_and_wait() then blocks on cache_clean_timer_exit until the original 600 s expiry fires, and qcow2_close() holds BQL the whole time so the VM stalls behind it. block_copy_kick() has the same shape. Fix the primitive once instead of working around it in each caller. Use a tri-state for QemuCoSleep::to_wake: NULL - idle co - sleeper parked PENDING - wake delivered, no sleeper yet (sticky) qemu_co_sleep_wake() xchgs PENDING into to_wake: a real sleeper is woken, NULL/PENDING is left untouched so the wake stays sticky. qemu_co_sleep() cmpxchg-publishes itself as the sleeper; if a wake was delivered before it got there or races the publish, the cmpxchg observes PENDING and returns without yielding. On normal resume qemu_co_sleep() clears the PENDING the waker left behind so the next sleep starts clean. A double-fire (real wake plus timer callback) is harmless: the first xchg returns the coroutine and wakes it; the second returns PENDING and is a no-op. Cancellation latency through qemu_co_sleep_wake() is now bounded by aio_co_wake() rather than by the sleep duration. Fixes: f86dde9a15 ("qcow2: Fix cache_clean_timer") Signed-off-by: Denis V. Lunev Cc: Hanna Czenczek Cc: Kevin Wolf Message-ID: <20260610115850.2410566-2-den@openvz.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/qemu/coroutine.h | 17 +++++++++--- tests/unit/test-coroutine.c | 53 +++++++++++++++++++++++++++++++++++++ util/qemu-coroutine-sleep.c | 53 ++++++++++++++++++++++++++----------- 3 files changed, 104 insertions(+), 19 deletions(-) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index e545bbf620..1c31de60f9 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -260,10 +260,19 @@ int coroutine_fn qemu_co_timeout(CoroutineEntry *entry, void *opaque, uint64_t timeout_ns, CleanupFunc clean); /** - * Wake a coroutine if it is sleeping in qemu_co_sleep_ns. The timer will be - * deleted. @sleep_state must be the variable whose address was given to - * qemu_co_sleep_ns() and should be checked to be non-NULL before calling - * qemu_co_sleep_wake(). + * Wake a coroutine sleeping in qemu_co_sleep() or qemu_co_sleep_ns_wakeable(). + * The timer set up by the latter is deleted on wakeup. + * + * The wake is sticky: if no sleeper is parked on @w at the time of the call, + * the wake is recorded on @w and consumed by the next qemu_co_sleep() on the + * same @w, which then returns without yielding. This closes the lost-wakeup + * window between two sleeps and is the documented behavior callers should + * rely on -- e.g. a cancellation signal raised between iterations of a + * sleep/work loop will shorten the next sleep instead of being dropped. + * + * The state persists until consumed: if no further qemu_co_sleep() is ever + * called on @w, the pending wake is harmlessly discarded when @w goes away. + * Multiple wakes coalesce -- the next sleep consumes at most one. */ void qemu_co_sleep_wake(QemuCoSleep *w); diff --git a/tests/unit/test-coroutine.c b/tests/unit/test-coroutine.c index 49d4d9b251..aa1f719b08 100644 --- a/tests/unit/test-coroutine.c +++ b/tests/unit/test-coroutine.c @@ -421,6 +421,57 @@ static void test_co_rwlock_downgrade(void) g_assert(c1_done); } +/* + * Check that a wake delivered before the sleeper parks is not lost. + * + * qemu_co_sleep_wake() is fire-and-forget: a caller cancelling a + * sleep/work loop may call it in the window after the sleeper has + * decided to sleep but before it has published itself inside + * qemu_co_sleep(). The wake must be sticky and shorten the next sleep + * rather than being dropped (which would block until the full sleep + * duration expired). + * + * No threads, timers or AioContext are needed: coroutines are + * cooperative, so ordering the wake before the sleep deterministically + * reproduces the state the racing waker would otherwise produce. + */ + +typedef struct { + QemuCoSleep w; + bool completed; +} CoSleepWakeData; + +static void coroutine_fn co_sleep_wake_entry(void *opaque) +{ + CoSleepWakeData *d = opaque; + + /* + * The wake was already delivered before we got here. qemu_co_sleep() + * must consume it and return without yielding. + */ + qemu_co_sleep(&d->w); + d->completed = true; +} + +static void test_co_sleep_wake_before_sleep(void) +{ + CoSleepWakeData d = { .w = { 0 }, .completed = false }; + Coroutine *co = qemu_coroutine_create(co_sleep_wake_entry, &d); + + /* Waker runs first, while no sleeper is parked on w. */ + qemu_co_sleep_wake(&d.w); + + /* + * Entering runs qemu_co_sleep(), which consumes the pending wake and + * returns without yielding, so the coroutine runs straight to + * completion in this single enter. With the pre-fix primitive the wake + * is dropped, qemu_co_sleep() parks, and completed stays false. + */ + qemu_coroutine_enter(co); + + g_assert(d.completed); +} + /* * Check that creation, enter, and return work */ @@ -660,6 +711,8 @@ int main(int argc, char **argv) g_test_add_func("/locking/co-mutex/lockable", test_co_mutex_lockable); g_test_add_func("/locking/co-rwlock/upgrade", test_co_rwlock_upgrade); g_test_add_func("/locking/co-rwlock/downgrade", test_co_rwlock_downgrade); + g_test_add_func("/locking/co-sleep/wake-before-sleep", + test_co_sleep_wake_before_sleep); if (g_test_perf()) { g_test_add_func("/perf/lifecycle", perf_lifecycle); g_test_add_func("/perf/nesting", perf_nesting); diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c index edef117284..19ded0b6fd 100644 --- a/util/qemu-coroutine-sleep.c +++ b/util/qemu-coroutine-sleep.c @@ -18,20 +18,29 @@ static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns"; +/* + * Sentinel stored in QemuCoSleep::to_wake by qemu_co_sleep_wake() when no + * sleeper has parked yet. The next qemu_co_sleep() consumes it and returns + * without yielding, so a wake that races the arming of a sleep is never + * lost. + */ +#define QEMU_CO_SLEEP_PENDING ((Coroutine *)(uintptr_t)1) + void qemu_co_sleep_wake(QemuCoSleep *w) { Coroutine *co; - co = w->to_wake; - w->to_wake = NULL; - if (co) { - /* Write of schedule protected by barrier write in aio_co_schedule */ - const char *scheduled = qatomic_cmpxchg(&co->scheduled, - qemu_co_sleep_ns__scheduled, NULL); - - assert(scheduled == qemu_co_sleep_ns__scheduled); - aio_co_wake(co); + co = qatomic_xchg(&w->to_wake, QEMU_CO_SLEEP_PENDING); + if (co == NULL || co == QEMU_CO_SLEEP_PENDING) { + /* No sleeper, or a wake is already pending. */ + return; } + + /* Write of scheduled protected by barrier write in aio_co_schedule */ + const char *scheduled = qatomic_cmpxchg(&co->scheduled, + qemu_co_sleep_ns__scheduled, NULL); + assert(scheduled == qemu_co_sleep_ns__scheduled); + aio_co_wake(co); } static void co_sleep_cb(void *opaque) @@ -43,6 +52,7 @@ static void co_sleep_cb(void *opaque) void coroutine_fn qemu_co_sleep(QemuCoSleep *w) { Coroutine *co = qemu_coroutine_self(); + Coroutine *prev; const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL, qemu_co_sleep_ns__scheduled); @@ -53,11 +63,23 @@ void coroutine_fn qemu_co_sleep(QemuCoSleep *w) abort(); } - w->to_wake = co; + /* + * Publish ourselves as the sleeper. A wake delivered before we got here, + * or one racing this publish, leaves QEMU_CO_SLEEP_PENDING in to_wake; + * the cmpxchg then fails and we consume the wake without yielding. + */ + prev = qatomic_cmpxchg(&w->to_wake, NULL, co); + if (prev == QEMU_CO_SLEEP_PENDING) { + qatomic_set(&w->to_wake, NULL); + qatomic_set(&co->scheduled, NULL); + return; + } + assert(prev == NULL); + qemu_coroutine_yield(); - /* w->to_wake is cleared before resuming this coroutine. */ - assert(w->to_wake == NULL); + /* The waker left QEMU_CO_SLEEP_PENDING; clear it for the next sleep. */ + qatomic_set(&w->to_wake, NULL); } void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, @@ -70,9 +92,10 @@ void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w, timer_mod(&ts, qemu_clock_get_ns(type) + ns); /* - * The timer will fire in the current AiOContext, so the callback - * must happen after qemu_co_sleep yields and there is no race - * between timer_mod and qemu_co_sleep. + * A wake racing with the arming of the sleep -- including the timer + * we just armed firing in another AioContext before qemu_co_sleep() + * publishes itself -- is captured by the sticky PENDING state in + * qemu_co_sleep_wake() and consumed here without yielding. */ qemu_co_sleep(w); timer_del(&ts); From b9ad1c4396e7eb376f0f8dc7d20538980cfa08f2 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 2 Jul 2026 15:22:35 +0200 Subject: [PATCH 256/860] block/export/fuse: fix regression with O_TRUNC when export is not growable Before commit a94a1d7699 ("fuse: Manually process requests (without libfuse)"), the O_TRUNC flag when open()-ing an export would be ignored. This is because libfuse sets FUSE_CAP_ATOMIC_O_TRUNC, so the kernel lets user space handle the O_TRUNC flag, which is ignored by the fuse code for export. After the commit, FUSE_CAP_ATOMIC_O_TRUNC is not set anymore, so the O_TRUNC flag is handled by the kernel, which executes a truncate. For blockdev-based exports, this causes a regression, because opening with O_TRUNC would previously work, but results in an ENOTSUP after commit a94a1d7699. For file-based exports, the fact that truncate is executed can be considered an improvement in general. However, in combination with growable=off, this still results in a practical regression in combination with virt-fw-vars, which opens its output file with O_TRUNC and previously worked with a file-based export with growable=off. After commit a94a1d7699, the file is truncated upon open and then cannot grow, meaning virt-fw-vars won't be able to write the output. To fix these regressions, while keeping the improved behavior for file-based exports with growable=on, set the FUSE_CAP_ATOMIC_O_TRUNC flag again if growable=off. Cc: qemu-stable@nongnu.org Fixes: a94a1d7699 ("fuse: Manually process requests (without libfuse)") Signed-off-by: Fiona Ebner Message-ID: <20260702132256.661429-2-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/export/fuse.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/block/export/fuse.c b/block/export/fuse.c index c0e8dfb643..9ae22a2e00 100644 --- a/block/export/fuse.c +++ b/block/export/fuse.c @@ -859,6 +859,18 @@ fuse_co_init(FuseExport *exp, struct fuse_init_out *out, uint32_t supported_flags = FUSE_ASYNC_READ | FUSE_ASYNC_DIO; uint32_t flags2 = 0; + if (!exp->growable) { + /* + * Back when libfuse was used, it would always set this flag and thus + * the kernel did not execute a truncate itself and passed along O_TRUNC + * to user space. Continue setting the flag for backwards compatibility + * when the export is not growable to avoid issues with O_TRUNC, i.e. + * blockdev-based exports running into ENOTSUP and file-based exports + * with growable=off to be truncated and then stuck with size 0. + */ + supported_flags = FUSE_ATOMIC_O_TRUNC; + } + if (in->major != 7) { error_report("FUSE major version mismatch: We have 7, but kernel has %" PRIu32, in->major); From 1d5e53df1674e0e45be50a4a873e6deede2eecd7 Mon Sep 17 00:00:00 2001 From: Minwoo Im Date: Wed, 29 Jul 2026 19:34:52 +0900 Subject: [PATCH 257/860] hw/nvme: drop AER requests without aiocb in nvme_del_sq() nvme_del_sq() asserted r->aiocb was always set when canceling a queue's inflight requests. A pending Async Event Request has no aiocb (nvme_aer() parks it without issuing any block I/O), so deleting a queue with an outstanding AER trips the assert instead of just dropping the request. Cc: qemu-stable@nongnu.org Signed-off-by: Minwoo Im Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index bd6ad64b20..e3eadf3d1d 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -4862,12 +4862,14 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req) sq = n->sq[qid]; while (!QTAILQ_EMPTY(&sq->out_req_list)) { r = QTAILQ_FIRST(&sq->out_req_list); - assert(r->aiocb); r->status = NVME_CMD_ABORT_SQ_DEL; - blk_aio_cancel(r->aiocb); - } - assert(QTAILQ_EMPTY(&sq->out_req_list)); + if (r->aiocb) { + blk_aio_cancel(r->aiocb); + } else { + QTAILQ_REMOVE(&sq->out_req_list, r, entry); + } + } if (!nvme_check_cqid(n, sq->cqid)) { cq = n->cq[sq->cqid]; From 849ea354ddc1c9a4c2af03d57f75acf106ff6b45 Mon Sep 17 00:00:00 2001 From: Minwoo Im Date: Wed, 29 Jul 2026 19:34:53 +0900 Subject: [PATCH 258/860] hw/nvme: factor out nvme_sq_cancel_inflight() Factor the cancel-and-wait loop used by nvme_del_sq() into nvme_sq_cancel_inflight(), so it can be reused to drain queues on controller reset. Cc: qemu-stable@nongnu.org Signed-off-by: Minwoo Im Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e3eadf3d1d..e355c256a5 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -4826,6 +4826,26 @@ static int nvme_init_sq_ioeventfd(NvmeSQueue *sq) return 0; } +/* + * A pending Async Event Request has no aiocb (nvme_aer() parks it without + * issuing any block I/O), so there is nothing to cancel; just drop it. + */ +static void nvme_sq_cancel_inflight(NvmeSQueue *sq, uint16_t status) +{ + NvmeRequest *r; + + while (!QTAILQ_EMPTY(&sq->out_req_list)) { + r = QTAILQ_FIRST(&sq->out_req_list); + r->status = status; + + if (r->aiocb) { + blk_aio_cancel(r->aiocb); + } else { + QTAILQ_REMOVE(&sq->out_req_list, r, entry); + } + } +} + static void nvme_free_sq(NvmeSQueue *sq, NvmeCtrl *n) { uint16_t offset = sq->sqid << 3; @@ -4860,16 +4880,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_del_sq(qid); sq = n->sq[qid]; - while (!QTAILQ_EMPTY(&sq->out_req_list)) { - r = QTAILQ_FIRST(&sq->out_req_list); - r->status = NVME_CMD_ABORT_SQ_DEL; - - if (r->aiocb) { - blk_aio_cancel(r->aiocb); - } else { - QTAILQ_REMOVE(&sq->out_req_list, r, entry); - } - } + nvme_sq_cancel_inflight(sq, NVME_CMD_ABORT_SQ_DEL); if (!nvme_check_cqid(n, sq->cqid)) { cq = n->cq[sq->cqid]; From 86f938333e2faae7c60ca66c0809f5495c59aa4b Mon Sep 17 00:00:00 2001 From: Minwoo Im Date: Wed, 29 Jul 2026 19:34:54 +0900 Subject: [PATCH 259/860] hw/nvme: cancel inflight requests on controller reset nvme_ctrl_reset() freed every SQ/CQ right after nvme_ns_drain(), which only waits out requests on a per-namespace BlockBackend. That is safe as long as the guest first tore down I/O queues gracefully (Delete I/O SQ/CQ), since nvme_del_sq() already cancels and waits for anything left on a queue before freeing it. A reset that happens without that graceful sequence first (e.g. an abrupt/asynchronous controller reset) can still have commands inflight on blk_aio_*. Freeing sq/cq before those complete leaves their completion callbacks (nvme_rw_cb() and friends) to run against already-freed NvmeRequest/NvmeSQueue/NvmeCQueue memory via nvme_enqueue_req_completion(), causing a use-after-free/segfault. Run nvme_sq_cancel_inflight() over every queue in nvme_ctrl_reset() before the free loops, so no in-flight blk_aio_* callback can fire after sq/cq memory is freed. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3398 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3883 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4068 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4072 Signed-off-by: Minwoo Im Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e355c256a5..8461b6fbde 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8040,6 +8040,18 @@ static void nvme_ctrl_reset(NvmeCtrl *n, NvmeResetType rst) nvme_ns_drain(ns); } + /* + * Cancel and wait out every inflight command on every queue first. A + * reset is not required to be preceded by the guest's graceful + * Delete I/O SQ/CQ sequence, so sq/cq must not be freed below while a + * blk_aio_* completion for them could still be in flight. + */ + for (i = 0; i < n->num_queues; i++) { + if (n->sq[i] != NULL) { + nvme_sq_cancel_inflight(n->sq[i], NVME_CMD_ABORT_SQ_DEL); + } + } + for (i = 0; i < n->num_queues; i++) { if (n->sq[i] != NULL) { nvme_free_sq(n->sq[i], n); From 7a34f7b8794b29cd1bd4dfa45f7d8e8daba7cff5 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Fri, 31 Jul 2026 11:21:33 +0200 Subject: [PATCH 260/860] hw/nvme: fix leak on copy ranges The buffer holding the ranges for the copy command is not correctly deallocated. Fix this. Cc: qemu-stable@nongnu.org Link: https://gitlab.com/qemu-project/qemu/-/work_items/4072 Fixes: 796d20681d9b ("hw/nvme: reimplement the copy command to allow aio cancellation") Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 8461b6fbde..4893cf7e74 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -2811,6 +2811,7 @@ static void nvme_copy_done(NvmeCopyAIOCB *iocb) qemu_iovec_destroy(&iocb->iov); g_free(iocb->bounce); + g_free(iocb->ranges); if (iocb->ret < 0) { block_acct_failed(stats, &iocb->acct.read); From 7d06bbb2d9ba77e45b5a3131c9e7f934df99d5bc Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 2 Jul 2026 15:22:36 +0200 Subject: [PATCH 261/860] iotests: test O_TRUNC behavior for fuse exports The test cases for the blockdev-based export and for the file-based export with growable=on work before commit a94a1d7699 ("fuse: Manually process requests (without libfuse)"), then are broken until commit "block/export/fuse: fix regression with O_TRUNC when export is growable". The test case for the blockdev-based export requires passwordless sudo for losetup and chmod similar to test 108. Signed-off-by: Fiona Ebner Message-ID: <20260702132256.661429-3-f.ebner@proxmox.com> [kwolf: Catch OSError when probing sudo support] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/fuse-truncate | 171 +++++++++++++++++++++ tests/qemu-iotests/tests/fuse-truncate.out | 5 + 2 files changed, 176 insertions(+) create mode 100755 tests/qemu-iotests/tests/fuse-truncate create mode 100644 tests/qemu-iotests/tests/fuse-truncate.out diff --git a/tests/qemu-iotests/tests/fuse-truncate b/tests/qemu-iotests/tests/fuse-truncate new file mode 100755 index 0000000000..7eee35111c --- /dev/null +++ b/tests/qemu-iotests/tests/fuse-truncate @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +# group: rw +# +# Test how fuse exports behave with regard to O_TRUNC. +# +# Copyright (C) 2026 Proxmox Server Solutions GmbH +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import subprocess +from pathlib import Path + +import iotests +from iotests import qemu_img, QemuStorageDaemon + +fuse_mount_point = os.path.join(iotests.test_dir, 'export.fuse') +image_size = 1 * 1024 * 1024 +image = os.path.join(iotests.test_dir, 'image.' + iotests.imgfmt) + +def check_fuse_support(): + Path(fuse_mount_point).touch() + test_qsd = QemuStorageDaemon('--blockdev', 'null-co,node-name=node0', + qmp=True) + res = test_qsd.qmp('block-export-add', { + 'id': 'exp0', + 'type': 'fuse', + 'node-name': 'node0', + 'mountpoint': fuse_mount_point, + 'allow-other': 'off' + }) + test_qsd.stop() + os.remove(fuse_mount_point) + if 'error' in res: + assert (res['error']['desc'] == + "Parameter 'type' does not accept value 'fuse'") + iotests.notrun('No FUSE support') + +def check_sudo_support(): + try: + subprocess.run(['sudo', '-n', 'losetup', '--version'], + capture_output=True, check=True) + except (OSError, subprocess.CalledProcessError): + return False + try: + subprocess.run(['sudo', '-n', 'chmod', '--version'], + capture_output=True, check=True) + except (OSError, subprocess.CalledProcessError): + return False + return True + +check_fuse_support() + +class TestTruncateBase(iotests.QMPTestCase): + growable = False + supports_preconditions = True + + def evaluate_preconditions(self): + return + + def add_blockdev(self): + qemu_img('create', '-f', iotests.imgfmt, image, str(image_size)) + self.qsd.cmd('blockdev-add', { + 'node-name': 'node0', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': image + } + }) + + def cleanup_blockdev(self): + os.remove(image) + + def add_export(self): + self.qsd.cmd('block-export-add', { + 'id': 'exp0', + 'type': 'fuse', + 'node-name': 'node0', + 'mountpoint': fuse_mount_point, + 'growable': self.growable, + 'writable': True, + 'allow-other': 'off' + }) + + def stop_qsd(self): + if self.qsd: + self.qsd.cmd('block-export-del', {'id': 'exp0'}) + self.qsd.stop() + self.qsd = None + + def setUp(self): + self.evaluate_preconditions() + if not self.supports_preconditions: + return + Path(fuse_mount_point).touch() + self.qsd = QemuStorageDaemon(qmp=True) + self.add_blockdev() + self.add_export() + + def tearDown(self): + if not self.supports_preconditions: + return + self.stop_qsd() + self.cleanup_blockdev() + os.remove(fuse_mount_point) + +class TestTruncateFileGrowable(TestTruncateBase): + growable = True + + def test_o_trunc(self): + with open(fuse_mount_point, 'w+b') as file: + file.seek(0, os.SEEK_END) + self.assertEqual(file.tell(), 0) + file.write(b"test") + self.stop_qsd() + +class TestTruncateFileNotGrowable(TestTruncateBase): + growable = False + + def test_o_trunc(self): + with open(fuse_mount_point, 'w+b') as file: + file.seek(0, os.SEEK_END) + self.assertEqual(file.tell(), image_size) + file.seek(0, os.SEEK_SET) + file.write(b"test") + self.stop_qsd() + +class TestTruncateBlockdev(TestTruncateBase): + growable = False + + def evaluate_preconditions(self): + self.supports_preconditions = check_sudo_support() + + def add_blockdev(self): + qemu_img('create', '-f', iotests.imgfmt, image, str(image_size)) + res = subprocess.run(['sudo', '-n', 'losetup', '--show', '-f', image], + check=True, capture_output=True, text=True) + self.loopdev = res.stdout.strip() + subprocess.run(['sudo', '-n', 'chmod', 'go+rw', self.loopdev], + check=True, capture_output=True) + self.qsd.cmd('blockdev-add', { + 'node-name': 'node0', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'host_device', + 'filename': self.loopdev + } + }) + + def cleanup_blockdev(self): + subprocess.run(['sudo', '-n', 'losetup', '--detach', self.loopdev], + check=True, capture_output=True) + os.remove(image) + + def test_o_trunc(self): + if not self.supports_preconditions: + iotests.case_notrun('No passwordless sudo for losetup and chmod') + return + + with open(fuse_mount_point, 'w+b') as file: + file.seek(0, os.SEEK_END) + self.assertEqual(file.tell(), image_size) + file.seek(0, os.SEEK_SET) + file.write(b"test") + self.stop_qsd() + +if __name__ == '__main__': + iotests.main(supported_fmts=['raw'], + supported_protocols=['file'], + supported_platforms=['linux']) diff --git a/tests/qemu-iotests/tests/fuse-truncate.out b/tests/qemu-iotests/tests/fuse-truncate.out new file mode 100644 index 0000000000..8d7e996700 --- /dev/null +++ b/tests/qemu-iotests/tests/fuse-truncate.out @@ -0,0 +1,5 @@ +... +---------------------------------------------------------------------- +Ran 3 tests + +OK From d00567f7fb2312c71bfc65a338c1b046f623df81 Mon Sep 17 00:00:00 2001 From: Christian Quante Date: Tue, 14 Jul 2026 18:40:30 +0200 Subject: [PATCH 262/860] hw/block/fdc: select the drive named by the READ ID command Every other command handler begins by latching the drive from the command byte: SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); fdctrl_handle_readid() does not, so it works on whichever drive happened to be selected last. A guest that issues READ ID for a drive other than the one currently selected gets an answer about the wrong one. It has gone unnoticed because a driver normally writes the DOR to spin up the motor first, and that write selects the drive as a side effect. The controller does not require it, though, and the command carries the drive number for a reason. Reported-by: Kevin Wolf Signed-off-by: Christian Quante Message-ID: <20260714164031.60551-2-christian@quante.one> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/block/fdc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 2c1681b7d0..9b2409cfa4 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -1936,7 +1936,10 @@ static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) { - FDrive *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv; + + SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); + cur_drv = get_cur_drv(fdctrl); cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; timer_mod(fdctrl->result_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + From 431f59a34db5234919262b558880a2d57b64a738 Mon Sep 17 00:00:00 2001 From: Christian Quante Date: Tue, 14 Jul 2026 18:40:31 +0200 Subject: [PATCH 263/860] hw/block/fdc: report a missing address mark on an empty drive READ ID on a drive with no medium terminates normally and returns the made-up sector ID left over from the "Pretend we are spinning" emulation. The only error path is a data rate mismatch, and media_rate is assigned solely by pick_geometry(); it is never reset when the medium is removed. A guest that has just ejected a diskette is therefore told that one is still present. READ, WRITE and FORMAT have a related problem: fd_seek() answers 2 both for "track/head out of range" and for "no medium", so the callers report ST0 = ABNTERM with ST1 = 0x00 either way. Without ST1.MA the guest cannot tell an absent diskette from a transient error. Give fd_seek() a return code of its own for an absent medium, and let both switch statements report the missing address mark for it. The comments on the two switches were swapped: fd_seek() answers 2 for a bad track or head and 3 for a sector past last_sect, but case 2 read "sect too big" and case 3 "track too big". Both now say what they mean. This is a behaviour change for FORMAT TRACK on an empty drive as well, which now answers ST1.MA rather than ST1 = 0x00. None of the guests tested reaches that path -- DOS gives up during media sensing and never issues the command -- but it seemed wrong to leave fdctrl_format_sector() falling through to "default" for a case fd_seek() now reports explicitly. Failing READ ID does not make guests detect the removal: real hardware never completes the command on an empty drive, because there are no index pulses, and OS/2 for one relies on that timeout. It does stop the controller from claiming a diskette that is not there. tests/qtest/fdc-test.c starts QEMU with "-device floppy,id=floppy0" and no medium, and test_read_id asserts a normal termination with a made-up cylinder 8 / head 1. That contradicts its neighbours test_no_media_on_start and test_media_change, which state that DSKCHG signals an absent medium. Insert a medium before READ ID and eject it afterwards -- the rewritten test passes before and after this change -- and add test_read_id_no_media for the empty drive. Guests checked, reading and writing, with and without a medium: Linux 2.0.34 and 7.0, PC-DOS 7, IBM DOS 5.02, Windows for Workgroups 3.11 and OS/2 2.11. None changes behaviour. No version of the Linux floppy driver from 1.2.13 to master issues READ ID at all -- FD_READID is defined in the uapi header for FDRAWCMD users and the driver never sends it -- so Linux detects an empty drive by stepping the head and reading DSKCHG instead. Buglink: https://gitlab.com/qemu-project/qemu/-/issues/3971 Signed-off-by: Christian Quante Message-ID: <20260714164031.60551-3-christian@quante.one> [kwolf: Added fd_seek() comment for new return value 5] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/block/fdc.c | 50 +++++++++++++++++++++++++----- tests/qtest/fdc-test.c | 70 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 14 deletions(-) diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 9b2409cfa4..1178b959a6 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -196,6 +196,12 @@ static void fd_init(FDrive *drv) #define NUM_SIDES(drv) ((drv)->flags & FDISK_DBL_SIDES ? 2 : 1) +/* Is a diskette present in the drive? */ +static bool fd_media_present(FDrive *drv) +{ + return drv->blk != NULL && blk_is_inserted(drv->blk); +} + static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect, uint8_t last_sect, uint8_t num_sides) { @@ -222,6 +228,7 @@ static int fd_offset(FDrive *drv) * returns 2 if track is invalid * returns 3 if sector is invalid * returns 4 if seek is disabled + * returns 5 if no floppy is inserted */ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, int enable_seek) @@ -258,7 +265,7 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, #endif drv->head = head; if (drv->track != track) { - if (drv->blk != NULL && blk_is_inserted(drv->blk)) { + if (fd_media_present(drv)) { drv->media_changed = 0; } ret = 1; @@ -267,8 +274,8 @@ static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, drv->sect = sect; } - if (drv->blk == NULL || !blk_is_inserted(drv->blk)) { - ret = 2; + if (!fd_media_present(drv)) { + ret = 5; } return ret; @@ -1476,14 +1483,24 @@ static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: - /* sect too big */ + /* track/head out of range */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; + case 5: + /* + * No medium: there is no address mark to be found. Guests that tell + * an absent diskette from an unreadable one rely on ST1.MA. + */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; case 3: - /* track too big */ + /* sector too big */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; @@ -1791,14 +1808,21 @@ static void fdctrl_format_sector(FDCtrl *fdctrl) NUM_SIDES(cur_drv))); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: - /* sect too big */ + /* track/head out of range */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, 0x00, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; fdctrl->fifo[5] = ks; return; + case 5: + /* no medium */ + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + fdctrl->fifo[3] = kt; + fdctrl->fifo[4] = kh; + fdctrl->fifo[5] = ks; + return; case 3: - /* track too big */ + /* sector too big */ fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_EC, 0x00); fdctrl->fifo[3] = kt; fdctrl->fifo[4] = kh; @@ -2306,6 +2330,18 @@ static void fdctrl_result_timer(void *opaque) FDCtrl *fdctrl = opaque; FDrive *cur_drv = get_cur_drv(fdctrl); + /* + * An empty drive has no address marks to read. Completing READ ID + * successfully, with the made-up sector ID left over from the "spinning" + * emulation below, tells the guest that a diskette is still present after + * it has been ejected. The only error path left was a data rate mismatch, + * and media_rate is never reset when the medium is removed. + */ + if (!fd_media_present(cur_drv)) { + FLOPPY_DPRINTF("read id on empty drive\n"); + fdctrl_stop_transfer(fdctrl, FD_SR0_ABNTERM, FD_SR1_MA, 0x00); + return; + } /* Pretend we are spinning. * This is needed for Coherent, which uses READ ID to check for * sector interleaving. diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c index 1b37a8a4d2..1e1dd8659d 100644 --- a/tests/qtest/fdc-test.c +++ b/tests/qtest/fdc-test.c @@ -64,6 +64,12 @@ enum { DSKCHG = 0x80, }; +enum { + ST0_IC_MASK = 0xc0, /* interrupt code */ + ST0_IC_ABNTERM = 0x40, /* abnormal termination */ + + ST1_MA = 0x01, /* missing address mark */ +}; static char *test_image; @@ -270,6 +276,21 @@ static void test_cmos(void) g_assert(cmos == 0x40 || cmos == 0x50); } +static void media_insert(void) +{ + qtest_qmp_assert_success(global_qtest, + "{'execute':'blockdev-change-medium', 'arguments':{" + " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", + test_image); +} + +static void media_eject(void) +{ + qtest_qmp_assert_success(global_qtest, + "{'execute':'eject', 'arguments':{" + " 'id':'floppy0' }}"); +} + static void test_no_media_on_start(void) { uint8_t dir; @@ -301,10 +322,7 @@ static void test_media_insert(void) /* Insert media in drive. DSKCHK should not be reset until a step pulse * is sent. */ - qtest_qmp_assert_success(global_qtest, - "{'execute':'blockdev-change-medium', 'arguments':{" - " 'id':'floppy0', 'filename': %s, 'format': 'raw' }}", - test_image); + media_insert(); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -333,9 +351,7 @@ static void test_media_change(void) /* Eject the floppy and check that DSKCHG is set. Reading it out doesn't * reset the bit. */ - qtest_qmp_assert_success(global_qtest, - "{'execute':'eject', 'arguments':{" - " 'id':'floppy0' }}"); + media_eject(); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); @@ -414,6 +430,9 @@ static void test_read_id(void) uint8_t st0; uint8_t msr; + /* READ ID reads an address mark, so it needs a medium in the drive. */ + media_insert(); + /* Seek to track 0 and check with READ ID */ send_seek(0); @@ -491,6 +510,42 @@ static void test_read_id(void) g_assert_cmpint(cyl, ==, 8); g_assert_cmpint(head, ==, 1); g_assert_cmpint(st0, ==, head << 2); + + /* Leave the drive empty, the way the machine starts up. */ + media_eject(); +} + +/* + * An empty drive spins no diskette, so READ ID finds no address mark and must + * terminate abnormally. Reporting success (with a made-up sector ID) would + * tell the guest that a medium is still present after it has been ejected. + */ +static void test_read_id_no_media(void) +{ + uint8_t drive = 0; + uint8_t head = 0; + uint8_t st0, st1; + + floppy_send(CMD_READ_ID); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(head << 2 | drive); + + while (!get_irq(FLOPPY_IRQ)) { + clock_step(1000000000LL / 50); + } + + st0 = floppy_recv(); + st1 = floppy_recv(); + floppy_recv(); /* ST2 */ + floppy_recv(); /* cylinder */ + floppy_recv(); /* head */ + floppy_recv(); /* sector */ + g_assert(get_irq(FLOPPY_IRQ)); + floppy_recv(); /* sector size */ + g_assert(!get_irq(FLOPPY_IRQ)); + + g_assert_cmpint(st0 & ST0_IC_MASK, ==, ST0_IC_ABNTERM); + g_assert_cmpint(st1 & ST1_MA, ==, ST1_MA); } static void test_read_no_dma_1(void) @@ -625,6 +680,7 @@ int main(int argc, char **argv) qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); qtest_add_func("/fdc/relative_seek", test_relative_seek); qtest_add_func("/fdc/read_id", test_read_id); + qtest_add_func("/fdc/read_id_no_media", test_read_id_no_media); qtest_add_func("/fdc/verify", test_verify); qtest_add_func("/fdc/media_insert", test_media_insert); qtest_add_func("/fdc/read_no_dma_1", test_read_no_dma_1); From 29922afeae84a6c51f35d569da97c0b8c8fcc165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 23 Jun 2026 17:03:26 +0100 Subject: [PATCH 264/860] iotests: increase timeouts for tests to 5 minutes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we have tests timeout set to 3 minutes, on the basis that they're generally done in less than a minute. I've hit a couple of random failures suggesting that's not sufficiently pessimistic. Increase the timeout to 5 minutes to have a greater safety net in high load scenarios. Signed-off-by: Daniel P. Berrangé Message-ID: <20260623160326.2346255-1-berrange@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/meson.build | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/qemu-iotests/meson.build b/tests/qemu-iotests/meson.build index bc6132a0f7..8857f25f29 100644 --- a/tests/qemu-iotests/meson.build +++ b/tests/qemu-iotests/meson.build @@ -52,16 +52,17 @@ foreach driver, speed: qemu_iotests_drivers '-tap', '-' + driver, item, '--source-dir', meson.current_source_dir(), '--build-dir', meson.current_build_dir()] - # Some individual tests take as long as 45 seconds - # Bump the timeout to 3 minutes for some headroom - # on slow machines to minimize spurious failures + # Some individual tests take as long as 3 minutes + # in high load scenarios. Bump the timeout to 5 + # minutes for some headroom on slow / loaded + # machines to minimize spurious failures test('io-' + driver + '-' + item, python, args: args, depends: qemu_iotests_binaries, env: qemu_iotests_env, protocol: 'tap', - timeout: 180, + timeout: 300, suite: suites) endforeach endif @@ -94,16 +95,14 @@ foreach driver, speed: qemu_iotests_drivers '-tap', '-' + driver, item, '--source-dir', meson.current_source_dir(), '--build-dir', meson.current_build_dir()] - # Some individual tests take as long as 45 seconds - # Bump the timeout to 3 minutes for some headroom - # on slow machines to minimize spurious failures + # See earlier note about timeouts test('io-' + driver + '-' + item, python, args: args, depends: qemu_iotests_binaries, env: qemu_iotests_env, protocol: 'tap', - timeout: 180, + timeout: 300, suite: suites) endforeach endforeach From 241095547a5d87ad6fa68cd674fe524e6596b958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 29 Jul 2026 20:14:31 +0400 Subject: [PATCH 265/860] hw/display/virtio-gpu: validate blob iov size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio_gpu_resource_create_blob() stores the guest-controlled blob_size without checking it against the total size of the iov backing entries. Since both values are independently guest-controlled, a malicious guest can set blob_size much larger than the actual iov backing. Subsequent SET_SCANOUT_BLOB checks bounds against the inflated blob_size, allowing a pixman surface to be created over the undersized buffer. Any display refresh then reads past the actual allocation, potentially crashing QEMU or leaking host memory contents depending on the backing type. Validate that the iov backing is at least as large as the declared blob_size in create_blob (when nr_entries > 0, since the spec permits deferred backing), attach_backing (when attaching to a blob resource), and the blob migration load path. Fixes: CVE-2026-66021 Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3945 Reported-by: "sundayjiang(蒋浩天)" Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Message-ID: <20260729161431.1180691-1-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 4d46a4eb10..0206910cc3 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -372,6 +372,17 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g, return; } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } + virtio_gpu_init_udmabuf(res); QTAILQ_INSERT_HEAD(&g->reslist, res, next); } @@ -993,6 +1004,15 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, return; } + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + return; + } + if (!res->image) { virtio_gpu_init_udmabuf(res); } @@ -1493,6 +1513,14 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, res->iov[i].iov_len = qemu_get_be32(f); } + if (res->iov_cnt > 0 && + iov_size(res->iov, res->iov_cnt) < res->blob_size) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + if (!virtio_gpu_load_restore_mapping(g, res)) { g_free(res); return -EINVAL; From 95687639e647ec917226e6d3a6713a2b373e1ffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 28 Jul 2026 19:14:56 +0400 Subject: [PATCH 266/860] hw/display/vga: fix panning_buf OOB after text/graphics switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fields last_width and last_height serve two purposes: the text renderer counts in characters, the graphics renderer in pixels. panning_buf reallocation is guarded by geometry-change check, so the unit mismatch can trick it into thinking nothing changed when the resolution actually grew. A guest can trigger this by switching graphics -> text -> graphics: 1. Enter graphics mode with a small width (CR01=0x00, 8 pixels). The predicate fires and panning_buf is allocated for that width. 2. Switch to text mode with a large width (CR01=0xFF, 256 chars). The text renderer stores 256 into last_width. The text path never touches panning_buf. 3. Switch back to graphics with a width that happens to equal 256 in pixels (CR01=0x1F, 32*8 = 256). The predicate sees 256 == 256 and skips the realloc. With horizontal pel panning enabled, vga_draw_line4() then writes a full 256-pixel scanline into the buffer still sized for 8 pixels -- a 960-byte heap overflow on every scanline, every refresh. Fix it by reallocating unconditionally panning_buf on vga_draw_graphic(). Fixes: CVE-2026-17516 Fixes: 973a724eb006 ("vga: implement horizontal pel panning in graphics modes") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4085 Cc: Paolo Bonzini Signed-off-by: Warisjeet Singh [ Marc- André - drop realloc() resize condition & commit message ] Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau Message-ID: <20260728151456.3704099-1-marcandre.lureau@redhat.com> --- hw/display/vga.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index abe3f8e077..da0c331486 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1647,11 +1647,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->last_line_offset = s->params.line_offset; s->last_depth = depth; s->last_byteswap = byteswap; - /* 16 extra pixels are needed for double-width planar modes. */ - s->panning_buf = g_realloc(s->panning_buf, - (disp_width + 16) * sizeof(uint32_t)); full_update = 1; } + + /* 16 extra pixels are needed for double-width planar modes. */ + s->panning_buf = g_realloc(s->panning_buf, + (disp_width + 16) * sizeof(uint32_t)); if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4) && !surface_is_allocated(surface)) { /* base address changed (page flip) -> shared display surfaces From a113e0c53fb50d78529fd5ea79e3b8313a7ddcaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 10 Jul 2026 17:47:19 +0400 Subject: [PATCH 267/860] vhost-user-gpu: fix integer overflow in buffer allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A malicious guest can trigger a heap buffer overflow in the vhost-user-gpu backend by sending a VIRTIO_GPU_CMD_RESOURCE_CREATE_2D with large width and height values (e.g. 65537x65537). The allocation size width * height * 4 silently wraps in uint32_t arithmetic, resulting in a much smaller allocation than expected. Subsequent VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D writes past the heap buffer. The in-tree virtio-gpu device (hw/display/virtio-gpu.c) already handles this via calc_image_hostmem() with uint64_t arithmetic and an overflow check. Apply the same approach to the vhost-user-gpu contrib backend: - Add an overflow check in vugbm_buffer_create() rejecting dimensions where width * height * 4 exceeds UINT32_MAX - Promote the size arithmetic to uint64_t in mem_alloc_bo() and udmabuf_get_size() - Check the return value of vugbm_buffer_create() in vg_resource_create_2d(), which was previously ignored Fixes: CVE-2026-15264 Reported-by: "Vulnerability Report" Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3940 Signed-off-by: Marc-Andre Lureau Acked-by: Philippe Mathieu-Daudé Message-ID: <20260710134720.2317856-1-marcandre.lureau@redhat.com> --- contrib/vhost-user-gpu/vhost-user-gpu.c | 8 +++++++- contrib/vhost-user-gpu/vugbm.c | 11 +++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index bb41758e34..ee9858c397 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -388,7 +388,13 @@ vg_resource_create_2d(VuGpu *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; } - vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height); + if (!vugbm_buffer_create(&res->buffer, &g->gdev, c2d.width, c2d.height)) { + g_critical("%s: buffer creation failed %d %d %d", + __func__, c2d.resource_id, c2d.width, c2d.height); + g_free(res); + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } res->image = pixman_image_create_bits(pformat, c2d.width, c2d.height, diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c index 503d0a4566..710d545297 100644 --- a/contrib/vhost-user-gpu/vugbm.c +++ b/contrib/vhost-user-gpu/vugbm.c @@ -13,7 +13,7 @@ static bool mem_alloc_bo(struct vugbm_buffer *buf) { - buf->mmap = g_malloc(buf->width * buf->height * 4); + buf->mmap = g_malloc((uint64_t)buf->width * buf->height * 4); buf->stride = buf->width * 4; return true; } @@ -53,7 +53,8 @@ struct udmabuf_create { static size_t udmabuf_get_size(struct vugbm_buffer *buf) { - return ROUND_UP(buf->width * buf->height * 4, qemu_real_host_page_size()); + return ROUND_UP((uint64_t)buf->width * buf->height * 4, + qemu_real_host_page_size()); } static bool @@ -293,6 +294,12 @@ bool vugbm_buffer_create(struct vugbm_buffer *buffer, struct vugbm_device *dev, uint32_t width, uint32_t height) { + uint64_t size = (uint64_t)width * height * 4; + if (size > UINT32_MAX) { + g_warning("buffer dimensions too large: %ux%u", width, height); + return false; + } + buffer->dev = dev; buffer->width = width; buffer->height = height; From b8ef970532c30da2f3fa8985867a74f898ce96aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 25 Jul 2026 16:27:34 +0400 Subject: [PATCH 268/860] hw/display/virtio-gpu: fix offset wraparound in scanout_blob_to_fb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio_gpu_scanout_blob_to_fb() computes the framebuffer offset from guest-controlled offsets[0], r.x, r.y and stride using uint32_t arithmetic. When the sum exceeds UINT32_MAX, silent wraparound lets the guest steer the scanout to an arbitrary in-bounds region of the blob instead of the intended rectangle. Compute the offset in uint64_t, reject values exceeding UINT32_MAX (the width of fb->offset), and only store into fb->offset once both range checks pass. ("[PATCH] hw/display/virtio-gpu: Remove the bytes_pp field") Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3871 Based-on: <20260719-bpp-v1-1-9b91946d6cf3@rsg.ci.i.u-tokyo.ac.jp> Reported-by: Cyber_black Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Message-ID: <20260725122734.1775774-1-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 0206910cc3..51592b49c2 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -777,7 +777,7 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, struct virtio_gpu_set_scanout_blob *ss, uint64_t blob_size) { - uint64_t fbend; + uint64_t fbend, offset; uint32_t bytes_pp; fb->format = virtio_gpu_get_pixman_format(ss->format); @@ -807,18 +807,20 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, return false; } - fb->offset = ss->offsets[0] + ss->r.x * bytes_pp + ss->r.y * fb->stride; + offset = (uint64_t)ss->offsets[0] + (uint64_t)ss->r.x * bytes_pp + + (uint64_t)ss->r.y * fb->stride; - fbend = fb->offset; - fbend += (uint64_t) fb->stride * ss->r.height; + fbend = offset + (uint64_t)fb->stride * ss->r.height; - if (fbend > blob_size) { + if (offset > UINT32_MAX || fbend > blob_size) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: fb end out of range\n", + "%s: invalid fb bounds\n", __func__); return false; } + fb->offset = offset; + return true; } From 555d0a1eba204085e20421cd93a55276a21b8e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 30 Jul 2026 15:47:51 +0400 Subject: [PATCH 269/860] hw/display/virtio-gpu: drop redundant node->value NULL checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QAPI-generated list visitors guarantee that node->value is never NULL: the input visitor allocates it via g_malloc0() in visit_start_struct(), and on failure the entire list parse is aborted and freed. Remove the unnecessary NULL checks from both callsites iterating g->conf.outputs. Resolves: Coverity CID 1664272 Fixes: 8dc8449a678f ("hw/display/virtio-gpu: Avoid leaking migration blocker") Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Message-ID: <20260730114751.3515083-1-marcandre.lureau@redhat.com> --- hw/display/virtio-gpu-base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index 946e56b42f..270fbaae10 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -69,7 +69,7 @@ virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, for (output_idx = 0, node = g->conf.outputs; output_idx <= scanout && node; output_idx++, node = node->next) { - if (output_idx == scanout && node->value && node->value->name) { + if (output_idx == scanout && node->value->name) { info.name = node->value->name; break; } @@ -206,7 +206,7 @@ virtio_gpu_base_device_realize(DeviceState *qdev, error_setg(errp, "invalid outputs > %d", g->conf.max_outputs); return false; } - if (node->value && node->value->name && + if (node->value->name && strlen(node->value->name) > EDID_NAME_MAX_LENGTH) { error_setg(errp, "invalid output name '%s' > %d", node->value->name, EDID_NAME_MAX_LENGTH); From 1f24066fc88d33455ee54a20f29994d9e69997ba Mon Sep 17 00:00:00 2001 From: Ankur Saini Date: Mon, 3 Aug 2026 22:39:21 +0530 Subject: [PATCH 270/860] virtio-gpu: reject requests with short/truncated control headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A short control request can leave command data partially initialized. For the common header, guest-controlled flags can then cause stale fence metadata to be returned to the guest. The command fill helpers detect a short copy but only log and return. For the common header this leaves the request without any completion; for type-specific commands the caller still completes the request but reports VIRTIO_GPU_RESP_OK_NODATA, masking the error. Make VIRTIO_GPU_FILL_CMD() clear the partially copied object and complete the request with ERR_INVALID_PARAMETER. Make VUGPU_FILL_CMD() report the same error through the existing vhost-user-gpu dispatcher. This also rejects truncated type-specific commands. The vhost-user-gpu common header is copied outside VUGPU_FILL_CMD(), so clear it and complete the request directly when that copy is short. Fixes: CVE-2026-18054 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4094 Reported-by: Ankur Saini Suggested-by: Akihiko Odaki Signed-off-by: Ankur Saini Reviewed-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260803-virtio-gpu-short-header-v3-1-936c1daa8e61@gmail.com> --- contrib/vhost-user-gpu/vhost-user-gpu.c | 21 ++++++++++++--------- contrib/vhost-user-gpu/vugpu.h | 1 + include/hw/virtio/virtio-gpu.h | 3 +++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index ee9858c397..7864881509 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -930,16 +930,19 @@ vg_handle_ctrl(VuDev *dev, int qidx) if (len != sizeof(cmd->cmd_hdr)) { g_warning("%s: command size incorrect %zu vs %zu\n", __func__, len, sizeof(cmd->cmd_hdr)); - } - - virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); - g_debug("%d %s\n", cmd->cmd_hdr.type, - vg_cmd_to_string(cmd->cmd_hdr.type)); - - if (vg->virgl) { - vg_virgl_process_cmd(vg, cmd); + memset(&cmd->cmd_hdr, 0, sizeof(cmd->cmd_hdr)); + vg_ctrl_response_nodata( + vg, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); } else { - vg_process_cmd(vg, cmd); + virtio_gpu_ctrl_hdr_bswap(&cmd->cmd_hdr); + g_debug("%d %s\n", cmd->cmd_hdr.type, + vg_cmd_to_string(cmd->cmd_hdr.type)); + + if (vg->virgl) { + vg_virgl_process_cmd(vg, cmd); + } else { + vg_process_cmd(vg, cmd); + } } if (cmd->state != VG_CMD_STATE_FINISHED) { diff --git a/contrib/vhost-user-gpu/vugpu.h b/contrib/vhost-user-gpu/vugpu.h index 2374eb90cb..aaf2870cb2 100644 --- a/contrib/vhost-user-gpu/vugpu.h +++ b/contrib/vhost-user-gpu/vugpu.h @@ -179,6 +179,7 @@ struct virtio_gpu_ctrl_command { if (vugpufillcmd_s_ != sizeof(out)) { \ g_critical("%s: command size incorrect %zu vs %zu", \ __func__, vugpufillcmd_s_, sizeof(out)); \ + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; \ return; \ } \ } while (0) diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 2f60c72078..f965defa6b 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -315,6 +315,9 @@ struct VirtIOGPURutabaga { qemu_log_mask(LOG_GUEST_ERROR, \ "%s: command size incorrect %zu vs %zu\n", \ __func__, virtiogpufillcmd_s_, sizeof(out)); \ + memset(&out, 0, sizeof(out)); \ + virtio_gpu_ctrl_response_nodata( \ + g, cmd, VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER); \ return; \ } \ } while (0) From d513c644b89361ab645a50a627a1d846aefc3eb2 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 3 Aug 2026 17:45:25 +0900 Subject: [PATCH 271/860] hw/display/virtio-gpu: Always reject invalid scanout bounds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio-gpu does not consistently check scanout bounds with wraparound handling. In the unchecked virgl SET_SCANOUT path, guest dimensions reach qemu_console_resize(), qemu_create_displaysurface(), and ultimately qemu_pixman_image_new_shareable(..., &error_abort), so an invalid rectangle can terminate QEMU. Implement a check with proper wraparound handling and apply it consistently. Fixes: 9d9e152136bd ("virtio-gpu: add 3d mode and virgl rendering support.") Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob") Fixes: 7c092f17ccee ("virtio-gpu: Handle resource blob commands") Fixes: 1dcc6adbc168 ("gfxstream + rutabaga: add initial support for gfxstream") Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-ID: <20260803-scanout-v1-1-c9831dafdab2@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu-rutabaga.c | 6 ++++++ hw/display/virtio-gpu-virgl.c | 20 ++++++++---------- hw/display/virtio-gpu.c | 36 +++++++++++++++++++------------- include/hw/virtio/virtio-gpu.h | 5 +++++ 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index e28aad94ee..a054f8117f 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -315,6 +315,12 @@ rutabaga_cmd_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) res = virtio_gpu_find_resource(g, ss.resource_id); CHECK(res, cmd); + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + res->width, res->height, &ss.r, + &cmd->error)) { + return; + } + if (!res->image) { pixman_format_code_t pformat; pformat = virtio_gpu_get_pixman_format(res->format); diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index d9e5b01104..6e298f997d 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -560,7 +560,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, } g->parent_obj.enable = 1; - if (ss.resource_id && ss.r.width && ss.r.height) { + if (ss.resource_id) { struct virgl_renderer_resource_info info; void *d3d_tex2d = NULL; @@ -581,6 +581,11 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + info.width, info.height, &ss.r, + &cmd->error)) { + return; + } qemu_console_resize(g->parent_obj.scanout[ss.scanout_id].con, ss.r.width, ss.r.height); virgl_renderer_force_ctx_0(); @@ -987,16 +992,9 @@ static void virgl_cmd_set_scanout_blob(VirtIOGPU *g, return; } - if (ss.width < 16 || - ss.height < 16 || - ss.r.x + ss.r.width > ss.width || - ss.r.y + ss.r.height > ss.height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, ss.scanout_id, ss.resource_id, - ss.r.x, ss.r.y, ss.r.width, ss.r.height, - ss.width, ss.height); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(ss.scanout_id, ss.resource_id, + ss.width, ss.height, &ss.r, + &cmd->error)) { return; } diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 51592b49c2..c15d4b527d 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -633,6 +633,26 @@ static uint32_t virtio_gpu_format_bytes_pp(pixman_format_code_t format) return DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8); } +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error) +{ + if (r->width < 16 || + r->height < 16 || + (uint64_t)r->x + r->width > width || + (uint64_t)r->y + r->height > height) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" + " resource %d, fb %d %d, rect (%d,%d)+%d,%d\n", + __func__, scanout_id, resource_id, width, height, + r->x, r->y, r->width, r->height); + *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return false; + } + + return true; +} + static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, uint32_t scanout_id, struct virtio_gpu_framebuffer *fb, @@ -646,20 +666,8 @@ static bool virtio_gpu_do_set_scanout(VirtIOGPU *g, scanout = &g->parent_obj.scanout[scanout_id]; - if (r->x > fb->width || - r->y > fb->height || - r->width < 16 || - r->height < 16 || - r->width > fb->width || - r->height > fb->height || - r->x + r->width > fb->width || - r->y + r->height > fb->height) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout %d bounds for" - " resource %d, rect (%d,%d)+%d,%d, fb %d %d\n", - __func__, scanout_id, res->resource_id, - r->x, r->y, r->width, r->height, - fb->width, fb->height); - *error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + if (!virtio_gpu_check_scanout_bounds(scanout_id, res->resource_id, + fb->width, fb->height, r, error)) { return false; } diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index f965defa6b..220231ec9d 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -366,6 +366,11 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, struct virtio_gpu_scanout *s, uint32_t resource_id); +bool virtio_gpu_check_scanout_bounds(uint32_t scanout_id, uint32_t resource_id, + uint32_t width, uint32_t height, + const struct virtio_gpu_rect *r, + uint32_t *error); + /** * virtio_gpu_scanout_blob_to_fb() - fill out fb based on scanout data * fb: the frame-buffer descriptor to fill out From 5ce01c92389a422389898df481ed441a80ece3c4 Mon Sep 17 00:00:00 2001 From: Bin Guo Date: Mon, 3 Aug 2026 16:21:58 +0800 Subject: [PATCH 272/860] hw/display/virtio-gpu: Unmap DMA regions on reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio_gpu_reset() freed in-flight commands without unmapping the DMA regions acquired by virtqueue_pop(). Call virtqueue_detach_element() before g_free() in both drain loops. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3467 Cc: qemu-stable@nongnu.org Signed-off-by: Bin Guo Reviewed-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260803082158.62998-1-guobin@linux.alibaba.com> --- hw/display/virtio-gpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index c15d4b527d..fbb6fec7a0 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1725,12 +1725,14 @@ void virtio_gpu_reset(VirtIODevice *vdev) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); QTAILQ_REMOVE(&g->cmdq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g_free(cmd); } while (!QTAILQ_EMPTY(&g->fenceq)) { cmd = QTAILQ_FIRST(&g->fenceq); QTAILQ_REMOVE(&g->fenceq, cmd, next); + virtqueue_detach_element(cmd->vq, &cmd->elem, 0); g->inflight--; g_free(cmd); } From dd272c8c87f165d41c945946242c5c8619e7122f Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 31 Jul 2026 17:50:01 +0200 Subject: [PATCH 273/860] qapi/dump: add allowed-by-guest feature to win-dmp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1c0e259c5a35 ("dump: make win_dump_available() check vmcoreinfo for a Windows dump header") changed two things in a way that is visible to QMP clients but not to introspection: query-dump-guest-memory-capability now lists win-dmp only for a guest that has published a Windows dump header through the vmcoreinfo device, and dump-guest-memory, which shares win_dump_available(), rejects the format otherwise. Before that, both accepted win-dmp on any x86 machine. A client that wants to select win-dmp automatically therefore cannot trust the capability query on its own: on an older QEMU it reports win-dmp for every x86 guest, Linux ones included, where the resulting dump is useless. libvirt ran into exactly this while picking a format for on_crash and watchdog triggered dumps, and has no way to tell the two behaviours apart. Add an 'allowed-by-guest' feature to the win-dmp member of DumpGuestMemoryFormat so the fixed behaviour becomes discoverable. DumpGuestMemoryFormat is reachable from both query-dump-guest-memory-capability's return type and dump-guest-memory's arguments, so a single flag covers both halves of the change. Where the feature is absent, a reported win-dmp says nothing about the guest, and a client that needs the dump to be loadable afterwards should fall back to elf. CC: Eric Blake CC: Markus Armbruster CC: "Marc-André Lureau" Suggested-by: Daniel P. Berrangé Signed-off-by: Denis V. Lunev Reviewed-by: Daniel P. Berrangé Reviewed-by: Marc-André Lureau Message-ID: <20260731155001.1204103-1-den@openvz.org> --- qapi/dump.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qapi/dump.json b/qapi/dump.json index 726b520870..690f3963fe 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -38,6 +38,13 @@ # @win-dmp: Windows full crashdump format, can be used instead of ELF # converting (since 2.13) # +# Features: +# +# @allowed-by-guest: If present, @win-dmp is listed by +# `query-dump-guest-memory-capability`, and accepted by +# `dump-guest-memory`, only when the guest has published a Windows +# dump header through the vmcoreinfo device (since 11.1) +# # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', @@ -45,7 +52,7 @@ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'kdump-raw-zlib', 'kdump-raw-lzo', 'kdump-raw-snappy', - 'win-dmp' ] } + { 'name': 'win-dmp', 'features': ['allowed-by-guest'] } ] } ## # @dump-guest-memory: From 9232220004434db1f3be5b2c8b1bba935b043acd Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 4 Aug 2026 12:54:14 -0400 Subject: [PATCH 274/860] gitlab: disable provenance attestations to work around CI bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU's CI pipeline involves building container images that will be used to run builds and tests. A recent Docker change triggered the following error: $ docker push "$TAG" ... error from registry: blob unknown to registry - sha256:4401f6f779caf8841cafd5f483e642fcac56a23a4e4a59523231e101c890dad9 https://gitlab.com/qemu-project/qemu/-/jobs/15701875927#L2372 This happens because Docker now pushes out-of-order and the GitLab Container Registry rejects due to an unknown reference: https://forum.gitlab.com/t/started-yesterday-docker-push-error-from-registry-blob-unknown-to-registry/134733/5 It is unclear at this point whether GitLab will modify the behavior of Container Registry or whether Docker will ship a fix. The current workaround is to disable the provenance attestation that is involved in this issue. QEMU's CI pipeline container images are used internally for testing and are not widely distributed. Provenance attestation can be disabled as there are no external consumers of these images. Expect to revert this commit in the future when GitLab or Docker have released their own fixes. Cc: Alex Bennée Cc: Daniel P. Berrangé Cc: Thomas Huth Reviewed-by: Daniel P. Berrangé Message-ID: <20260804165414.480435-1-stefanha@redhat.com> Signed-off-by: Stefan Hajnoczi --- .gitlab-ci.d/container-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/container-template.yml b/.gitlab-ci.d/container-template.yml index 8c7311cca5..88317d650e 100644 --- a/.gitlab-ci.d/container-template.yml +++ b/.gitlab-ci.d/container-template.yml @@ -14,7 +14,7 @@ - echo "TAG:$TAG" - echo "COMMON_TAG:$COMMON_TAG" - docker build --tag "$TAG" --cache-from "$TAG" --cache-from "$COMMON_TAG" - --build-arg BUILDKIT_INLINE_CACHE=1 + --build-arg BUILDKIT_INLINE_CACHE=1 --provenance=false -f "tests/docker/dockerfiles/$NAME.docker" "." - docker push "$TAG" after_script: From 3e3ccab106f879b1512f8e0d51a827dd4de30e22 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 4 Aug 2026 15:36:08 -0400 Subject: [PATCH 275/860] Update version for v11.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 5399c18e4a..63d29a3de9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.0.92 +11.0.93 From 0177efb9b75891717c45667e20efe6d6e64f7c82 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 24 Jul 2026 10:18:44 -0700 Subject: [PATCH 276/860] target/hexagon: accept valid packets rejected by check has_valid_slot_assignment() rejected any packet with two instructions assigned to the same slot. That is too strict. When a memory instruction is encoded before a slot-flexible instruction in a packet, the descending slot assignment places the memory op in slot 1 and the other in slot 0, then the "mem insns to slot 0" fixup moves the memory op to slot 0 as well, leaving both in slot 0. Such a packet is valid and executes correctly, but the uniqueness test flagged it as HEX_CAUSE_INVALID_PACKET, raising SIGILL in linux-user and a precise exception in system mode. For example this packet, with the load encoded first, was wrongly rejected: { r6 = memw(r3+#-4) r7 = #0x4ae6 } Replace the uniqueness test with a slot-exhaustion check: walk the instructions in encoding order handing out slots in strictly decreasing order and fail only if an instruction has no valid slot at or below the running slot. This accepts packets that legally share a slot while still rejecting genuinely unassignable packets, such as a memory instruction grouped with a duplex, or a load followed by an instruction that requires a high slot. Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- target/hexagon/decode.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index 6eddcca26e..e6bb7733be 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -549,21 +549,35 @@ static bool decode_parsebits_is_loopend(uint32_t encoding32) return bits == 0x2; } +/* + * Check that the packet's instructions can be grouped into slots: walk them + * in encoding order handing out slots in strictly decreasing order, and fail + * if an instruction has no valid slot at or below the running slot. Two + * instructions may legally share a slot, so this does not require unique + * slots, only that every instruction fits. + */ static bool has_valid_slot_assignment(Packet *pkt) { - int used_slots = 0; - for (int i = 0; i < pkt->num_insns; i++) { - int slot_mask; - Insn *insn = &pkt->insn[i]; - if (decode_opcode_ends_loop(insn->opcode)) { + int i; + int slot = 3; + + for (i = 0; i < pkt->num_insns; i++) { + SlotMask valid_slots; + if (decode_opcode_ends_loop(pkt->insn[i].opcode)) { /* We overload slot 0 for endloop. */ continue; } - slot_mask = 1 << insn->slot; - if (used_slots & slot_mask) { + if (slot < 0) { return false; } - used_slots |= slot_mask; + valid_slots = get_valid_slots(pkt, i); + while (!(valid_slots & (1 << slot))) { + if (slot <= 0) { + return false; + } + slot--; + } + slot--; } return true; } From d2504d4cea329dcec69af4d590cbaa490c409724 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 24 Jul 2026 10:18:45 -0700 Subject: [PATCH 277/860] tests/tcg/hexagon: add slot-assignment tests valid-slots: packets that legally share a slot and were wrongly rejected before the fix (load and transfer, load encoded first; dczeroa packed last with three transfers). invalid-slots: unassignable packets that must still be rejected: store + duplex, load + indirect jump, three logical ops competing for slots 2 and 3, and five ops for four slots. Reviewed-by: Matheus Tavares Bernardino Signed-off-by: Brian Cain --- tests/tcg/hexagon/Makefile.target | 1 + tests/tcg/hexagon/invalid-slots.c | 72 +++++++++++++++++++++++++++++++ tests/tcg/hexagon/valid-slots.c | 62 ++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 tests/tcg/hexagon/valid-slots.c diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index a2a0ffc69b..80e56f7d05 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -51,6 +51,7 @@ HEX_TESTS += scatter_gather HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram HEX_TESTS += invalid-slots +HEX_TESTS += valid-slots HEX_TESTS += invalid-encoding HEX_TESTS += multiple-writes HEX_TESTS += unaligned_pc diff --git a/tests/tcg/hexagon/invalid-slots.c b/tests/tcg/hexagon/invalid-slots.c index 607027f314..f2dace2e54 100644 --- a/tests/tcg/hexagon/invalid-slots.c +++ b/tests/tcg/hexagon/invalid-slots.c @@ -55,6 +55,75 @@ static int test_invalid_slots(void) return sig; } +/* Load then indirect jump, load encoded first: no high slot left for jump. */ +static int test_invalid_slots_highslot(void) +{ + int sig; + + asm volatile( + "r0 = #0\n" + "r1 = ##1f\n" + "memw(%1) = r1\n" + "r3 = #mem\n" + ".word 0x91834006\n" /* { r6 = memw(r3+#0); */ + ".word 0x529fc000\n" /* jumpr r31 } */ + "1:\n" + "%0 = r0\n" + : "=r"(sig) + : "r"(&resume_pc) + : "r0", "r1", "r3", "r6", "memory"); + + return sig; +} + +/* + * Three predicate-logical ops: each is restricted to slots 2 and 3, so the + * fourth-and-fifth-slot-free packet still has only two slots for three ops. + * No change-of-flow is involved, so the only reason to reject it is the slot + * conflict. + */ +static int test_invalid_slots_crslot23(void) +{ + int sig; + + asm volatile( + "r0 = #0\n" + "r1 = ##1f\n" + "memw(%1) = r1\n" + ".word 0x6b024100\n" /* { p0 = and(p1, p2); */ + ".word 0x6b224103\n" /* p3 = or(p1, p2); */ + ".word 0x6b42c301\n" /* p1 = xor(p2, p3) } */ + "1:\n" + "%0 = r0\n" + : "=r"(sig) + : "r"(&resume_pc) + : "r0", "r1", "p0", "p1", "p3", "memory"); + + return sig; +} + +/* Three transfers plus a duplex: five ops for four slots. */ +static int test_invalid_slots_five(void) +{ + int sig; + + asm volatile( + "r0 = #0\n" + "r1 = ##1f\n" + "memw(%1) = r1\n" + ".word 0x78004020\n" /* { r0 = #1; */ + ".word 0x78004041\n" /* r1 = #2; */ + ".word 0x78004062\n" /* r2 = #3; */ + ".word 0x28452856\n" /* r5 = #4; r6 = #5 } */ + "1:\n" + "%0 = r0\n" + : "=r"(sig) + : "r"(&resume_pc) + : "r0", "r1", "r2", "r5", "r6", "memory"); + + return sig; +} + int main() { struct sigaction act; @@ -65,6 +134,9 @@ int main() assert(sigaction(SIGILL, &act, NULL) == 0); assert(test_invalid_slots() == SIGILL); + assert(test_invalid_slots_highslot() == SIGILL); + assert(test_invalid_slots_crslot23() == SIGILL); + assert(test_invalid_slots_five() == SIGILL); puts("PASS"); return EXIT_SUCCESS; diff --git a/tests/tcg/hexagon/valid-slots.c b/tests/tcg/hexagon/valid-slots.c new file mode 100644 index 0000000000..70d9b0b6ac --- /dev/null +++ b/tests/tcg/hexagon/valid-slots.c @@ -0,0 +1,62 @@ +/* + * Regression tests for valid packets that qemu incorrectly rejected as + * invalid. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +int err; + +#include "hex_test.h" + +/* volatile to keep the load from being optimized away */ +static volatile int buf[2] = { 0x1234, 0 }; + +/* Load and register transfer in one packet, load encoded first. */ +static int32_t load_imm_pair(void) +{ + int32_t out; + /* { r6 = memw(r3+#-4); r7 = #0x4ae6 } */ + asm volatile( + "{ r3 = %1 }\n\t" + ".word 0x97837fe6\n\t" + ".word 0x7845dcc7\n\t" + "{ %0 = r6 }\n\t" + : "=r"(out) : "r"(&buf[1]) : "r3", "r6", "r7"); + return out; +} + +static int32_t dcbuf[8] __attribute__((aligned(32))); + +/* Slot-0-only op (dczeroa) packed last with three transfers. */ +static void slot0_restricted(int32_t *out) +{ + asm volatile( + "{ %0 = #0x11\n\t" + " %1 = #0x22\n\t" + " %2 = #0x33\n\t" + " dczeroa(%3) }\n\t" + : "=r"(out[0]), "=r"(out[1]), "=r"(out[2]) + : "r"(dcbuf) : "memory"); +} + +int main() +{ + int32_t r[3]; + + check32(load_imm_pair(), 0x1234); + + dcbuf[0] = 0x5a5a5a5a; + slot0_restricted(r); + check32(r[0], 0x11); + check32(r[1], 0x22); + check32(r[2], 0x33); + check32(dcbuf[0], 0); /* dczeroa cleared the line */ + + puts(err ? "FAIL" : "PASS"); + return err; +} From 8f5bf4fa0aa2f5648f016b8265d86d4b05b42307 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 07:02:00 -0700 Subject: [PATCH 278/860] target/hexagon: don't let an idef-parser dest clobber its own source The idef-parser emitters write the destination in place, so when a packet is short-circuited and get_result_gpr() returns hex_gpr[] itself, an instruction naming one register as both source and destination reads back a value it already overwrote. `Rd32=cmpy(Rs32,Rt32):<<1:rnd:sat` with Rs == Rd is an example. Give a source reg that aliases a destination its own copy of the register value. Reviewed-by: Pierrick Bouvier Signed-off-by: Brian Cain --- target/hexagon/gen_tcg_funcs.py | 15 ++++++++ target/hexagon/genptr.c | 11 ++++++ target/hexagon/genptr.h | 1 + target/hexagon/hex_common.py | 12 +++++++ tests/tcg/hexagon/read_write_overlap.c | 47 ++++++++++++++++++++++++++ 5 files changed, 86 insertions(+) diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 6d5d99cee3..43e6807749 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -75,6 +75,21 @@ def gen_tcg_func(f, tag, regs, imms): f.write(f" int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n") if hex_common.is_idef_parser_enabled(tag): + gpr_operands = [ + hex_common.get_register(tag, regtype, regid) + for regtype, regid in regs + if hex_common.get_register(tag, regtype, regid).may_alias_gpr() + ] + dests = [reg for reg in gpr_operands if reg.is_written()] + for reg in gpr_operands: + if reg.is_written() or not reg.is_read(): + continue + src = reg.reg_tcg() + for dest in dests: + f.write(hex_common.code_fmt(f"""\ + {src} = gen_unalias_gpr_src({src}, {dest.reg_tcg()}); + """)) + declared = [] ## Handle registers for regtype, regid in regs: diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 3f31037709..12813f48bf 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -91,6 +91,17 @@ TCGv get_result_gpr(DisasContext *ctx, int rnum) } } +TCGv gen_unalias_gpr_src(TCGv src, TCGv dst) +{ + if (src != dst) { + return src; + } + + TCGv tmp = tcg_temp_new(); + tcg_gen_mov_tl(tmp, src); + return tmp; +} + static TCGv_i64 get_result_gpr_pair(DisasContext *ctx, int rnum) { TCGv_i64 result = tcg_temp_new_i64(); diff --git a/target/hexagon/genptr.h b/target/hexagon/genptr.h index 45ee038ca9..5d09677410 100644 --- a/target/hexagon/genptr.h +++ b/target/hexagon/genptr.h @@ -36,6 +36,7 @@ void gen_store8i(TCGv_env cpu_env, TCGv vaddr, int64_t src, uint32_t slot); TCGv gen_read_reg(TCGv result, int num); TCGv gen_read_preg(TCGv pred, uint8_t num); TCGv get_result_gpr(DisasContext *ctx, int rnum); +TCGv gen_unalias_gpr_src(TCGv src, TCGv dst); TCGv get_result_pred(DisasContext *ctx, int pnum); void gen_pred_write(DisasContext *ctx, int pnum, TCGv val); void gen_set_usr_field(DisasContext *ctx, int field, TCGv val); diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index d91a653c3d..09a025ba13 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -388,6 +388,8 @@ class Register: """)) def idef_arg(self, declared): declared.append(self.reg_tcg()) + def may_alias_gpr(self): + return False def helper_arg(self): return HelperArg( self.helper_proto_type(), @@ -495,6 +497,8 @@ class ReadWrite: return False class GprDest(Register, Single, Dest): + def may_alias_gpr(self): + return True def decl_tcg(self, f, tag, regno): self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ @@ -510,6 +514,8 @@ class GprDest(Register, Single, Dest): """)) class GprSource(Register, Single, OldSource): + def may_alias_gpr(self): + return True def decl_tcg(self, f, tag, regno): self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ @@ -531,6 +537,8 @@ class GprNewSource(Register, Single, NewSource): """)) class GprReadWrite(Register, Single, ReadWrite): + def may_alias_gpr(self): + return True def decl_tcg(self, f, tag, regno): self.decl_reg_num(f, regno) f.write(code_fmt(f"""\ @@ -557,6 +565,8 @@ class GprReadWrite(Register, Single, ReadWrite): """)) class ControlDest(Register, Single, Dest): + def may_alias_gpr(self): + return True def decl_reg_num(self, f, regno): f.write(code_fmt(f"""\ const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_SA0; @@ -593,6 +603,8 @@ class ControlSource(Register, Single, OldSource): """)) class ModifierSource(Register, Single, OldSource): + def may_alias_gpr(self): + return True def decl_reg_num(self, f, regno): f.write(code_fmt(f"""\ const int {self.reg_num} = insn->regno[{regno}] + HEX_REG_M0; diff --git a/tests/tcg/hexagon/read_write_overlap.c b/tests/tcg/hexagon/read_write_overlap.c index 95c54ccd63..7eaf75f545 100644 --- a/tests/tcg/hexagon/read_write_overlap.c +++ b/tests/tcg/hexagon/read_write_overlap.c @@ -115,12 +115,59 @@ static void test_swiz(void) check32(swiz(0x11223344), 0x44332211); } +#define CMPY(NAME, ASM) \ +static inline uint32_t NAME##_rd_eq_rs(uint32_t x, uint32_t y) \ +{ \ + uint32_t res; \ + asm("r7 = %1\n\t" \ + ASM("r7", "%2") "\n\t" \ + "%0 = r7\n\t" \ + : "=r"(res) : "r"(x), "r"(y) : "r7"); \ + return res; \ +} \ +static inline uint32_t NAME##_rd_eq_rt(uint32_t x, uint32_t y) \ +{ \ + uint32_t res; \ + asm("r7 = %2\n\t" \ + ASM("%1", "r7") "\n\t" \ + "%0 = r7\n\t" \ + : "=r"(res) : "r"(x), "r"(y) : "r7"); \ + return res; \ +} + +#define CMPY_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "):rnd:sat" +#define CMPY_S1_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "):<<1:rnd:sat" +#define CMPYC_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "*):rnd:sat" +#define CMPYC_S1_RND_SAT(RS, RT) "r7 = cmpy(" RS "," RT "*):<<1:rnd:sat" + +CMPY(cmpyrs_s0, CMPY_RND_SAT) +CMPY(cmpyrs_s1, CMPY_S1_RND_SAT) +CMPY(cmpyrsc_s0, CMPYC_RND_SAT) +CMPY(cmpyrsc_s1, CMPYC_S1_RND_SAT) + +static void test_cmpy(void) +{ + check32(cmpyrs_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x011b105b); + check32(cmpyrs_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x011b105b); + check32(cmpyrs_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x023520b5); + check32(cmpyrs_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x023520b5); + check32(cmpyrsc_s0_rd_eq_rs(0x32195ce2, 0xef862430), 0x0d0f09e8); + check32(cmpyrsc_s0_rd_eq_rt(0x32195ce2, 0xef862430), 0x0d0f09e8); + check32(cmpyrsc_s1_rd_eq_rs(0x32195ce2, 0xef862430), 0x1a1f13d0); + check32(cmpyrsc_s1_rd_eq_rt(0x32195ce2, 0xef862430), 0x1a1f13d0); + + /* Both halves saturate */ + check32(cmpyrs_s1_rd_eq_rs(0x80008000, 0x80008000), 0x7fff0000); + check32(cmpyrsc_s1_rd_eq_rs(0x7fff8001, 0x80017fff), 0x00008000); +} + int main() { test_insert(); test_insert_rp(); test_asr_r_svw_trun(); test_swiz(); + test_cmpy(); puts(err ? "FAIL" : "PASS"); return err ? EXIT_FAILURE : EXIT_SUCCESS; From bc4681990bb6b065e78e7564cb431e44dd148b88 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 5 Aug 2026 18:23:04 +0200 Subject: [PATCH 279/860] linux-user/sh4: Deliver SIGILL on invalid instruction On invalid instruction, deliver SIGILL rather than crashing the whole process unconditionally. Cc: qemu-stable@nongnu.org Signed-off-by: Mikulas Patocka Reviewed-by: Yoshinori Sato Reviewed-by: Richard Henderson Signed-off-by: Helge Deller --- linux-user/sh4/cpu_loop.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index ee2958d0d9..0815b9c9bf 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -64,6 +64,13 @@ void cpu_loop(CPUSH4State *env) cpu_exec_step_atomic(cs); arch_interrupt = false; break; + case 0x180: + /* Illegal instruction */ + /* fallthrough */ + case 0x1a0: + /* Illegal instruction in delay slot */ + force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->pc); + break; default: fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, 0); From 668d571bf0c2ee4851be2187798f413025647fc3 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 4 Aug 2026 10:59:18 +0200 Subject: [PATCH 280/860] linux-user/sh4: Initialize the FPSCR register on signal On the SH4 architecture, the instructions that perform single precision and double precision floating point operations are encoded in the same way. The bit PR in the FPSCR register determines if the CPU performs single or double operation. According to the ABI, the PR bit must be set at function entry and function exit. GCC generates code that flips this bit as needed during function execution. If we get a signal, we must set the PR bit, so that the signal handler finds the bit in the expected state. Qemu lacked this logic, so that if the signal interrupts single-precision floating point calculation, the PR bit would be incorrectly clear at signal handler entry. If the signal handler performed some floating-point calculation, it would get incorrect result. This patch fixes the bug, by initializing the FPSCR register at signal entry. Note that we initialize the whole register, because the Linux kernel initializes the whole register too. Cc: qemu-stable@nongnu.org Signed-off-by: Mikulas Patocka Reviewed-by: Yoshinori Sato Signed-off-by: Helge Deller --- linux-user/sh4/signal.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index 00290d6e40..7f246e750d 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -206,6 +206,8 @@ void setup_frame(int sig, struct target_sigaction *ka, __put_user(set->sig[i + 1], &frame->extramask[i]); } + regs->fpscr = FPSCR_PR; + /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { @@ -258,6 +260,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } + regs->fpscr = FPSCR_PR; + /* Set up to return from userspace. If provided, use a stub already in userspace. */ if (ka->sa_flags & TARGET_SA_RESTORER) { From f7ad7b1f8c8148f39bc1f2c984bfc7b7c9c59041 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Mon, 3 Aug 2026 21:38:32 +0200 Subject: [PATCH 281/860] linux-user/sh4: Fix crashes on signal delivery in conditional delay slot If we get a signal in the delay slot, we must roll-back the PC to the jump instruction. This was already fixed by the commit 3b894b699c9a ("linux-user/sh4: Fix crashes on signal delivery"), however this fix omits a test for TB_FLAG_DELAY_SLOT_COND. TB_FLAG_DELAY_SLOT_COND is set by the conditional delayed branches bf/s and bt/s. Qemu did not roll-back the PC in this case, resulting in incorrect program execution. This patch fixes it. Cc: qemu-stable@nongnu.org Signed-off-by: Mikulas Patocka Reviewed-by: Yoshinori Sato Signed-off-by: Helge Deller --- linux-user/sh4/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index 7f246e750d..047174ac8f 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -109,7 +109,7 @@ static void unwind_gusa(CPUSH4State *regs) the SP, otherwise we would be pushing the signal context to invalid memory. */ regs->gregs[15] = regs->gregs[1]; - } else if (regs->flags & TB_FLAG_DELAY_SLOT) { + } else if (regs->flags & (TB_FLAG_DELAY_SLOT | TB_FLAG_DELAY_SLOT_COND)) { /* If we are in a delay slot, push the previous instruction. */ regs->pc -= 2; } From 4713988da1c3ed7150014c87c2429d7a940b77c8 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 5 Aug 2026 13:16:05 -0400 Subject: [PATCH 282/860] linux-user: implement mount_setattr(2) mount_setattr() was in the syscall tables but had no implementation, so guests always got -ENOSYS. systemd uses it when setting up per-unit credential mounts, which fails the affected units with EXIT_CREDENTIALS. struct mount_attr is an extensible struct like open_how, so handle it the same way openat2() does: reject sizes smaller than the ver0 struct and require any unknown trailing bytes to be zero. All of its fields are 64-bit, and the MOUNT_ATTR_* and MS_* propagation values are identical on every target, so only the byte order needs fixing up. Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/strace.list | 3 +++ linux-user/syscall.c | 44 +++++++++++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 13 ++++++++++++ 3 files changed, 60 insertions(+) diff --git a/linux-user/strace.list b/linux-user/strace.list index 25ace05187..e952f15d20 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1737,3 +1737,6 @@ #ifdef TARGET_NR_fspick { TARGET_NR_fspick, "fspick", "%s(%d,%s,%d)", NULL, NULL }, #endif +#ifdef TARGET_NR_mount_setattr +{ TARGET_NR_mount_setattr, "mount_setattr", "%s(%d,%s,%d,%p,%d)", NULL, NULL }, +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index dc028686f4..cfa68dfbdb 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -9735,6 +9735,13 @@ _syscall5(int, sys_move_mount, int, __from_dfd, const char *, __from_pathname, int, __to_dfd, const char *, __to_pathname, unsigned int, flag) #endif +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) +#define __NR_sys_mount_setattr __NR_mount_setattr +_syscall5(int, sys_mount_setattr, int, dfd, const char *, path, + unsigned int, flags, struct mount_attr_ver0 *, uattr, + size_t, usize) +#endif + #if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) #define __NR_sys_fsopen __NR_fsopen _syscall2(int, sys_fsopen, const char *, fs_name, unsigned int, flags); @@ -14480,6 +14487,43 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, return do_map_shadow_stack(cpu_env, arg1, arg2, arg3); #endif +#if defined(TARGET_NR_mount_setattr) && defined(__NR_mount_setattr) + case TARGET_NR_mount_setattr: + { + struct mount_attr_ver0 attr = {}; + abi_ulong usize = arg5; + + if (usize < sizeof(struct target_mount_attr_ver0)) { + return -TARGET_EINVAL; + } + ret = copy_struct_from_user(&attr, sizeof(attr), arg4, usize); + if (ret) { + if (ret == -TARGET_E2BIG) { + qemu_log_mask(LOG_UNIMP, + "Unimplemented mount_setattr mount_attr " + "size: " TARGET_ABI_FMT_lu "\n", usize); + } + return ret; + } + /* + * MOUNT_ATTR_* and the MS_* propagation flags have the same + * values on all targets, so only byte order needs fixing up. + */ + attr.attr_set = tswap64(attr.attr_set); + attr.attr_clr = tswap64(attr.attr_clr); + attr.propagation = tswap64(attr.propagation); + attr.userns_fd = tswap64(attr.userns_fd); + + p = lock_user_string(arg2); + if (!p) { + return -TARGET_EFAULT; + } + ret = get_errno(sys_mount_setattr(arg1, p, arg3, &attr, + sizeof(attr))); + unlock_user(p, arg2, 0); + } + return ret; +#endif #if defined(TARGET_NR_fsopen) && defined(__NR_fsopen) case TARGET_NR_fsopen: { diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index e033c7db34..e28853c93b 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2770,6 +2770,19 @@ struct target_open_how_ver0 { abi_ullong mode; abi_ullong resolve; }; +/* from kernel's include/uapi/linux/mount.h */ +struct mount_attr_ver0 { + uint64_t attr_set; + uint64_t attr_clr; + uint64_t propagation; + uint64_t userns_fd; +}; +struct target_mount_attr_ver0 { + abi_ullong attr_set; + abi_ullong attr_clr; + abi_ullong propagation; + abi_ullong userns_fd; +}; #ifndef RESOLVE_NO_MAGICLINKS #define RESOLVE_NO_MAGICLINKS 0x02 #endif From 01b4e06b5225787139dae760e8b4fe0c39e5c04d Mon Sep 17 00:00:00 2001 From: Saul Freedman Date: Sun, 14 Jun 2026 23:33:38 -0400 Subject: [PATCH 283/860] target/i386/kvm: Fix CET SSP MSR read feature check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kvm_get_msrs() adds U_CET and S_CET when CET shadow stacks or IBT are enabled, but it checks FEAT_7_0_EDX with the ECX-defined CPUID_7_0_ECX_CET_SHSTK bit before adding the PL0-PL3 SSP MSRs. CPUID_7_0_ECX_CET_SHSTK belongs to FEAT_7_0_ECX. The current check therefore skips MSR_IA32_PL0_SSP through MSR_IA32_PL3_SSP even when guest shadow stacks are enabled. kvm_put_msrs() already uses FEAT_7_0_ECX for the same shadow-stack condition. Use the same feature word in kvm_get_msrs() so QEMU does not restore CET enablement with stale or zero SSP values. Fixes: b6f85c5e4527 ("i386/kvm: Add save/restore support for CET MSRs") Cc: qemu-stable@nongnu.org Signed-off-by: Saul Freedman Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20260615033338.1563854-1-fre3dm4n@gmail.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 4272b6770c..644c45fb0a 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5022,7 +5022,7 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, MSR_IA32_U_CET, 0); kvm_msr_entry_add(cpu, MSR_IA32_S_CET, 0); - if (env->features[FEAT_7_0_EDX] & CPUID_7_0_ECX_CET_SHSTK) { + if (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_CET_SHSTK) { kvm_msr_entry_add(cpu, MSR_IA32_PL0_SSP, 0); kvm_msr_entry_add(cpu, MSR_IA32_PL1_SSP, 0); kvm_msr_entry_add(cpu, MSR_IA32_PL2_SSP, 0); From 88dfd97b8acfe41cac7304fa1b6034952f046d1d Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 5 Aug 2026 13:23:29 -0400 Subject: [PATCH 284/860] linux-user: support writing floating-point registers to a core dump Write an NT_FPREGSET note for targets that opt in: a target_elf.h that defines HAVE_ELF_CORE_FPREGS and target_elf_fpregset_t, and supplies an elf_core_copy_fpregs() beside the existing elf_core_copy_regs(). Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/elfload.c | 19 +++++++++++++++++++ linux-user/loader.h | 3 +++ 2 files changed, 22 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e7c56af8ed..88508deac5 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1887,6 +1887,17 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, size_t sz, off_t offset) bswap_phdr(phdr, 1); } +#ifdef HAVE_ELF_CORE_FPREGS +static void fill_fpregset_note(void *data, CPUState *cpu) +{ + /* Fill locally and copy: note memory is only aligned to 4. */ + target_elf_fpregset_t fpregs = {}; + + elf_core_copy_fpregs(&fpregs, cpu_env(cpu)); + memcpy(data, &fpregs, sizeof(fpregs)); +} +#endif + static void fill_prstatus_note(void *data, CPUState *cpu, int signr) { /* @@ -2166,6 +2177,9 @@ static int elf_core_dump(int signr, const CPUArchState *env) offset += size_note("CORE", ts->info->auxv_len); offset += size_note("CORE", sizeof(struct target_elf_prpsinfo)); offset += size_note("CORE", sizeof(struct target_elf_prstatus)) * cpus; +#ifdef HAVE_ELF_CORE_FPREGS + offset += size_note("CORE", sizeof(target_elf_fpregset_t)) * cpus; +#endif note_size = offset - note_offset; data_offset = TARGET_PAGE_ALIGN(offset); @@ -2222,6 +2236,11 @@ static int elf_core_dump(int signr, const CPUArchState *env) dptr = fill_note(&hptr, NT_PRSTATUS, "CORE", sizeof(struct target_elf_prstatus)); fill_prstatus_note(dptr, cpu_iter, cpu_iter == cpu ? signr : 0); +#ifdef HAVE_ELF_CORE_FPREGS + dptr = fill_note(&hptr, NT_FPREGSET, "CORE", + sizeof(target_elf_fpregset_t)); + fill_fpregset_note(dptr, cpu_iter); +#endif } if (dump_write(fd, header, data_offset) < 0) { diff --git a/linux-user/loader.h b/linux-user/loader.h index da9ad28db5..5ddd140276 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -109,6 +109,9 @@ bool init_guest_commpage(void); struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); +/* Only defined by a target whose target_elf.h sets HAVE_ELF_CORE_FPREGS. */ +struct target_elf_fpregset_t; +void elf_core_copy_fpregs(struct target_elf_fpregset_t *, const CPUArchState *); typedef struct { const uint8_t *image; From 2304a655296aa3bf5e416cf5ce33e481fb87f73a Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 5 Aug 2026 13:15:03 -0400 Subject: [PATCH 285/860] linux-user/alpha: write the floating-point registers to a core dump A guest core carried only the general-purpose registers, so a debugger opening one reported every floating-point register as unavailable -- including the arguments of the function that crashed. Implement HAVE_ELF_CORE_FPREGS for Alpha: define target_elf_fpregset_t to match the kernel's layout ($f0-$f30 plus the control register in the slot $f31 would occupy) and fill it from elf_core_copy_fpregs(). Checked with lldb on a core from a program that faults with live values in $f16 and $f17: both read back correctly, as does the control register. Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/alpha/elfload.c | 10 ++++++++++ linux-user/alpha/target_elf.h | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c index 7be9e466b6..c2517516ad 100644 --- a/linux-user/alpha/elfload.c +++ b/linux-user/alpha/elfload.c @@ -32,3 +32,13 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "ev67"; } + +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUAlphaState *env) +{ + int i; + + for (i = 0; i < 31; i++) { + r->fpr[i] = tswap64(env->fir[i]); + } + r->fpcr = tswap64(cpu_alpha_load_fpcr((CPUAlphaState *)env)); +} diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index dd90c6f783..5efe7f3174 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -19,6 +19,17 @@ * r0-r30 at indices 0-30, pc at 31, ps at 32. * r31 (hardwired zero) is not stored; pc occupies index 31. */ +/* + * The floating-point note holds $f0 through $f30 and then the control + * register in the slot $f31 would occupy; $f31 reads as zero. + */ +#define HAVE_ELF_CORE_FPREGS 1 + +typedef struct target_elf_fpregset_t { + uint64_t fpr[31]; /* $f0-$f30 */ + uint64_t fpcr; /* the slot for $f31 */ +} target_elf_fpregset_t; + typedef struct target_elf_gregset_t { abi_ulong regs[31]; /* integer registers r0-r30 [0..30] */ abi_ulong pc; /* program counter [31] */ From bc22871fc0146f3cf6f4bbef9b6cdef16d1643f5 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 5 Aug 2026 13:15:04 -0400 Subject: [PATCH 286/860] linux-user/mips: write the floating-point registers to a core dump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Write an NT_FPREGSET note for MIPS and MIPS64, matching the kernel's elf_fpregset_t layout (ELF_NFPREG = 33): fpr[0..31] hold f0-f31, and fcsr occupies the low 32 bits of slot 32. The pad field rounds the struct to 33 × 8 bytes = 264 bytes. Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/mips/elfload.c | 8 ++++++++ linux-user/mips/target_elf.h | 13 +++++++++++++ linux-user/mips64/target_elf.h | 13 +++++++++++++ 3 files changed, 34 insertions(+) diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index ce2c4514f3..1d62ae0a4d 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -130,6 +130,14 @@ const char *get_elf_base_platform(CPUState *cs) #undef MATCH_PLATFORM_INSN +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUMIPSState *env) +{ + for (int i = 0; i < 32; i++) { + r->fpr[i] = tswap64(env->active_fpu.fpr[i].d); + } + r->fcsr = tswap32(env->active_fpu.fcr31); +} + /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ #ifndef TARGET_MIPS64 void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 157306f7a0..426f905d3a 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -26,4 +26,17 @@ typedef struct target_elf_gregset_t { }; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches the kernel's elf_fpregset_t (ELF_NFPREG = 33): + * fpr[0..31] hold f0-f31; fcsr occupies the low 32 bits of slot 32. + * pad rounds the struct to 33 × 8 bytes = 264 bytes. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; + uint32_t fcsr; + uint32_t pad; +} target_elf_fpregset_t; + #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 061471a0f1..efb5d97563 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -32,4 +32,17 @@ typedef struct target_elf_gregset_t { }; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches the kernel's elf_fpregset_t (ELF_NFPREG = 33): + * fpr[0..31] hold f0-f31; fcsr occupies the low 32 bits of slot 32. + * pad rounds the struct to 33 × 8 bytes = 264 bytes. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; + uint32_t fcsr; + uint32_t pad; +} target_elf_fpregset_t; + #endif From 474ad686693c8faeba34fd9e46ec81c98bb015ab Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 5 Aug 2026 13:15:05 -0400 Subject: [PATCH 287/860] linux-user/hppa: write the floating-point registers to a core dump Write an NT_FPREGSET note for HPPA, matching the kernel's elf_fpregset_t layout (ELF_NFPREG = 32): fr0-fr31 as 64-bit values. Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/hppa/elfload.c | 7 +++++++ linux-user/hppa/target_elf.h | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index ff4301b2ed..dd5b0b380e 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -17,6 +17,13 @@ const char *get_elf_platform(CPUState *cs) return "PARISC"; } +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUArchState *env) +{ + for (int i = 0; i < 32; i++) { + r->fpr[i] = tswap64(env->fr[i]); + } +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUArchState *env) { int i; diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 22547b1437..4357873aff 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -39,4 +39,13 @@ typedef struct target_elf_gregset_t { #define STACK_ALIGNMENT 64 #define VDSO_HEADER "vdso.c.inc" +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches the kernel's elf_fpregset_t (ELF_NFPREG = 32): fr0-fr31. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; +} target_elf_fpregset_t; + #endif From 0f183237d24daac28b6290b0605cba7b47aa8052 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 5 Aug 2026 13:15:06 -0400 Subject: [PATCH 288/860] linux-user/riscv: write the floating-point registers to a core dump Write an NT_FPREGSET note for RISC-V, matching struct __riscv_d_ext_state from uapi/asm/ptrace.h: f0-f31 as 64-bit values followed by fcsr. Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/riscv/elfload.c | 9 +++++++++ linux-user/riscv/target_elf.h | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c index afe103a631..1bc8beacb1 100644 --- a/linux-user/riscv/elfload.c +++ b/linux-user/riscv/elfload.c @@ -11,6 +11,15 @@ const char *get_elf_cpu_model(uint32_t eflags) return "max"; } +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPURISCVState *env) +{ + for (int i = 0; i < 32; i++) { + r->fpr[i] = tswap64(env->fpr[i]); + } + r->fcsr = tswap32(((uint32_t)env->frm << FSR_RD_SHIFT) | + (riscv_cpu_get_fflags((CPURISCVState *)env) << FSR_AEXC_SHIFT)); +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPURISCVState *env) { r->pc = tswapal(env->pc); diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 859f726578..185b81a2db 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -27,4 +27,15 @@ typedef struct target_elf_gregset_t { abi_ulong regs[31]; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches struct __riscv_d_ext_state from uapi/asm/ptrace.h: + * f0-f31 as 64-bit values followed by fcsr. + */ +typedef struct target_elf_fpregset_t { + uint64_t fpr[32]; + uint32_t fcsr; +} target_elf_fpregset_t; + #endif From ce276ab1a92cad460930fada1bc2e2e4a96f7595 Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Wed, 5 Aug 2026 13:15:07 -0400 Subject: [PATCH 289/860] linux-user/sh4: write the floating-point registers to a core dump Write an NT_FPREGSET note for SH4, matching struct user_fpu_struct from arch/sh/include/asm/user.h: fr0-fr15, xf0-xf15, fpscr, fpul. Signed-off-by: Matt Turner Reviewed-by: Helge Deller Signed-off-by: Helge Deller --- linux-user/sh4/elfload.c | 10 ++++++++++ linux-user/sh4/target_elf.h | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index f03ce49e7d..41601626ca 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -52,6 +52,16 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcap; } +void elf_core_copy_fpregs(target_elf_fpregset_t *r, const CPUSH4State *env) +{ + for (int i = 0; i < 16; i++) { + r->fpregs[i] = tswap32(env->fregs[i]); + r->xfpregs[i] = tswap32(env->fregs[16 + i]); + } + r->fpscr = tswap32(env->fpscr); + r->fpul = tswap32(env->fpul); +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { for (int i = 0; i < 16; i++) { diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index 3fcb63d409..5923022036 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -25,4 +25,16 @@ typedef struct target_elf_gregset_t { struct target_pt_regs pt; } target_elf_gregset_t; +#define HAVE_ELF_CORE_FPREGS 1 + +/* + * Matches struct user_fpu_struct from arch/sh/include/asm/user.h. + */ +typedef struct target_elf_fpregset_t { + uint32_t fpregs[16]; + uint32_t xfpregs[16]; + uint32_t fpscr; + uint32_t fpul; +} target_elf_fpregset_t; + #endif From aae77f5dddef62da7e1479c257d99d2ab574df56 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 14 Jul 2026 21:17:18 +0200 Subject: [PATCH 290/860] target/s390x: Make PRNO TRNG interruptible fill_buf_random() writes the entire guest-requested amount of random bytes in one go. Since the length is a full 64-bit value, a guest can request several gigabytes and keep the vCPU spinning inside the helper, without a chance to react to interrupts. Do the same thing as HELPER(mvcl): check cpu_loop_exit_requested() at the bottom of the loop, and when a return to the main loop is pending, stop and report partial completion with condition code 3. Reported-by: Christian Borntraeger Fixes: 3dbc5fdacb5a ("target/s390x: support PRNO_TRNG instruction") Cc: qemu-stable@nongnu.org Reviewed-by: Richard Henderson Signed-off-by: Ilya Leoshkevich Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/qemu-devel/20260714191948.342204-2-iii@linux.ibm.com Signed-off-by: Eric Farman --- target/s390x/tcg/crypto_helper.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 8fe0a22219..6a5dbe1caf 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c @@ -16,6 +16,7 @@ #include "qemu/guest-random.h" #include "s390x-internal.h" #include "tcg_s390x.h" +#include "exec/cpu-common.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" @@ -242,8 +243,8 @@ static int cpacf_sha512(CPUS390XState *env, const int mmu_idx, uintptr_t ra, return !len ? 0 : 3; } -static void fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, - uint64_t *buf_reg, uint64_t *len_reg) +static int fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, + uint64_t *buf_reg, uint64_t *len_reg) { const MemOpIdx oi = make_memop_idx(MO_8, mmu_idx); uint8_t tmp[256]; @@ -265,7 +266,13 @@ static void fill_buf_random(CPUS390XState *env, const int mmu_idx, uintptr_t ra, --*len_reg; } len -= block; + + if (cpu_loop_exit_requested(env_cpu(env))) { + break; + } } + + return len == 0 ? 0 : 3; } uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, @@ -278,6 +285,7 @@ uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, uint8_t subfunc[16] = { 0 }; uint64_t param_addr; MemOpIdx oi; + int cc; switch (type) { case S390_FEAT_TYPE_KMAC: @@ -308,9 +316,13 @@ uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3, return cpacf_sha512(env, mmu_idx, ra, env->regs[1], &env->regs[r2], &env->regs[r2 + 1], type); case 114: /* CPACF_PRNO_TRNG */ - fill_buf_random(env, mmu_idx, ra, &env->regs[r1], &env->regs[r1 + 1]); - fill_buf_random(env, mmu_idx, ra, &env->regs[r2], &env->regs[r2 + 1]); - break; + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r1], &env->regs[r1 + 1]); + if (cc == 0) { + cc = fill_buf_random(env, mmu_idx, ra, + &env->regs[r2], &env->regs[r2 + 1]); + } + return cc; default: /* we don't implement any other subfunction yet */ g_assert_not_reached(); From abe1d893f41d5fcbdabc22f46ec148069a4ef319 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 14 Jul 2026 21:17:19 +0200 Subject: [PATCH 291/860] tests/tcg/s390x: Test PRNO TRNG interruptibility Add a small test that issues a large PRNO TRNG request while a timer is running, and checks that the timer interrupts it several times. Signed-off-by: Ilya Leoshkevich Reviewed-by: Harald Freudenberger Link: https://lore.kernel.org/qemu-devel/20260714191948.342204-3-iii@linux.ibm.com Signed-off-by: Eric Farman --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/prno-trng.c | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/tcg/s390x/prno-trng.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 0ca030ded0..48cba4cdfa 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -75,6 +75,7 @@ Z13_TESTS+=vcksm Z13_TESTS+=vstl Z13_TESTS+=vrep Z13_TESTS+=precise-smc-user +Z13_TESTS+=prno-trng $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) diff --git a/tests/tcg/s390x/prno-trng.c b/tests/tcg/s390x/prno-trng.c new file mode 100644 index 0000000000..43eea5db1d --- /dev/null +++ b/tests/tcg/s390x/prno-trng.c @@ -0,0 +1,67 @@ +/* + * Test that PERFORM RANDOM NUMBER OPERATION TRNG is interruptible. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include +#include +#include +#include + +static unsigned char buf1[16 * 1024 * 1024]; +static unsigned char buf2[16 * 1024 * 1024]; + +static volatile sig_atomic_t interrupted; + +static void sigprof_handler(int sig, siginfo_t *info, void *ucontext) +{ + struct ucontext *uc = ucontext; + unsigned long addr = uc->uc_mcontext.regs.psw.addr; + + if (*(unsigned short *)(addr - 4) == 0xb93c) { + interrupted++; + } +} + +static void prno_trng(void *b1, unsigned long l1, void *b2, unsigned long l2) +{ + register unsigned long r0 asm("r0") = 114; /* TRNG */ + register unsigned long r2 asm("r2") = (unsigned long)b1; + register unsigned long r3 asm("r3") = l1; + register unsigned long r4 asm("r4") = (unsigned long)b2; + register unsigned long r5 asm("r5") = l2; + + asm volatile("0: ppno %[r2],%[r4]\n" /* prno alias for old toolchains */ + " jo 0b" + : [r2] "+r" (r2), [r3] "+r" (r3) + , [r4] "+r" (r4), [r5] "+r" (r5) + : "r" (r0) + : "cc", "memory"); +} + +int main(void) +{ + struct itimerval it = { + .it_interval = { .tv_usec = 10000 }, /* 0.01s */ + .it_value = { .tv_usec = 10000 }, + }; + struct sigaction act = { + .sa_sigaction = sigprof_handler, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGPROF, &act, NULL); + assert(err == 0); + err = setitimer(ITIMER_PROF, &it, NULL); + assert(err == 0); + + prno_trng(buf1, sizeof(buf1), buf2, sizeof(buf2)); + printf("interrupted %d times\n", interrupted); + assert(interrupted >= 3); + + return EXIT_SUCCESS; +} From 0103cb1cd175a070f52ed0ca4fe0712c2ba2befc Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 14 Jul 2026 21:02:40 +0200 Subject: [PATCH 292/860] target/s390x: Fix DR/D INT64_MIN / -1 host crash helper_divs32() divides the 64-bit dividend by the 32-bit divisor as a 64-bit host operation, guarding only against a zero divisor. INT64_MIN / -1 therefore overflows the host division before the representability check runs; on hosts that trap this, QEMU is killed with SIGFPE instead of raising the fixed-point-divide exception the guest expects: qemu-s390x: QEMU internal SIGFPE {code=INTDIV, addr=...} helper_divs64() already guards the same case; add the missing check to helper_divs32(). Reported-by: Christian Borntraeger Fixes: b4e2bd3563af ("target-s390: Send signals for divide") Cc: qemu-stable@nongnu.org Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Link: https://lore.kernel.org/qemu-devel/20260714190351.337923-2-iii@linux.ibm.com Signed-off-by: Eric Farman --- target/s390x/tcg/int_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c index fbda396f5b..5aedd1405b 100644 --- a/target/s390x/tcg/int_helper.c +++ b/target/s390x/tcg/int_helper.c @@ -39,7 +39,8 @@ uint64_t HELPER(divs32)(CPUS390XState *env, int64_t a, int64_t b64) int32_t b = b64; int64_t q, r; - if (b == 0) { + /* Catch divide by zero, and non-representable quotient (MIN / -1). */ + if (b == 0 || (b == -1 && a == (1ll << 63))) { tcg_s390_program_interrupt(env, PGM_FIXPT_DIVIDE, GETPC()); } From aa5d03bc93514ceed8d94f38dbdf951f6cb19d23 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 14 Jul 2026 21:02:41 +0200 Subject: [PATCH 293/860] tests/tcg/s390x: Test DR overflow (INT64_MIN / -1) Check that DR with a non-representable quotient raises SIGFPE rather than crashing the emulator. Signed-off-by: Ilya Leoshkevich Reviewed-by: Richard Henderson Link: https://lore.kernel.org/qemu-devel/20260714190351.337923-3-iii@linux.ibm.com Signed-off-by: Eric Farman --- tests/tcg/s390x/div.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/tcg/s390x/div.c b/tests/tcg/s390x/div.c index 6ad9900e08..124c9ecc33 100644 --- a/tests/tcg/s390x/div.c +++ b/tests/tcg/s390x/div.c @@ -1,6 +1,15 @@ #include +#include #include +/* Set asynchronously by the signal handler. */ +static volatile int signum; + +static void signal_handler(int n) +{ + signum = n; +} + static void test_dr(void) { register int32_t r0 asm("r0") = -1; @@ -65,11 +74,39 @@ static void test_dlgr(void) assert(r == 1); } +/* + * The most negative dividend divided by -1 yields a quotient that does not + * fit into 32 bits, so DR must raise a fixed-point-divide exception. + */ +static void test_dr_overflow(void) +{ + struct sigaction act = { .sa_handler = signal_handler }; + register int32_t r0 asm("r0"); + register int32_t r1 asm("r1"); + int32_t b = -1; + int err; + + err = sigaction(SIGFPE, &act, NULL); + assert(err == 0); + signum = -1; + + r0 = 0x80000000; + r1 = 0; + asm volatile("dr %[r0],%[b]" + : [r0] "+r" (r0), [r1] "+r" (r1) + : [b] "r" (b) + : "cc"); + assert(signum == SIGFPE); + + signal(SIGFPE, SIG_DFL); +} + int main(void) { test_dr(); test_dlr(); test_dsgr(); test_dlgr(); + test_dr_overflow(); return 0; } From 4e994ebb01442d90e97e2f94f53f61b1b0b34f62 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 27 Jul 2026 13:50:48 +0200 Subject: [PATCH 294/860] hw/char/sclpconsole-lm: avoid guest triggerable assert If a guest uses incorrect message length it can trigger an assert in process_mdb which kills the guest instead of reporting an error. Fix this by adding the correct length check. Fixes: 6a444f8507 ("s390/sclplmconsole: Add support for SCLP line-mode console") Cc: qemu-stable@nongnu.org Signed-off-by: Christian Borntraeger Reviewed-by: Matthew Rosato Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20260727115052.24289-2-borntraeger@linux.ibm.com [farman@linux.ibm.com: Fixed typo in commit message, added qemu-stable] Signed-off-by: Eric Farman --- hw/char/sclpconsole-lm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index 9a16896d22..f6ed282f1b 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -243,7 +243,8 @@ static int write_event_data(SCLPEvent *event, EventBufferHeader *ebh) SCLPConsoleLM *scon = SCLPLM_CONSOLE(event); len = be16_to_cpu(data->mdb.header.length); - if (len < sizeof(data->mdb.header)) { + if (len < sizeof(data->mdb.header) || + len > be16_to_cpu(data->header.length) - sizeof(EventBufferHeader)) { return SCLP_RC_INCONSISTENT_LENGTHS; } len -= sizeof(data->mdb.header); From df607fd056044e40352a43f0b4422b9a5cb015c8 Mon Sep 17 00:00:00 2001 From: Joshua Daley Date: Mon, 27 Jul 2026 13:50:49 +0200 Subject: [PATCH 295/860] s390x/ipl: validate num_comp against iplb length before iterating In ipl_valid_pv_components(), the upper bound of the for loop, ipib_pv->num_comp, is read from guest memory. Before iterating, verify that its value will not cause a read beyond the end of the IplParameterBlock. Fixes: c3347ed0d2ee42a7 ("s390x: protvirt: Support unpack facility") Cc: qemu-stable@nongnu.org Signed-off-by: Joshua Daley Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20260727115052.24289-3-borntraeger@linux.ibm.com [farman@linux.ibm.com: Added qemu-stable] Signed-off-by: Eric Farman --- hw/s390x/ipl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index fac30763df..ef9c063d90 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -124,6 +124,12 @@ static inline bool ipl_valid_pv_components(IplParameterBlock *iplb) return false; } + if (offsetof(IplParameterBlock, pv.components) + + ipib_pv->num_comp * sizeof(IPLBlockPVComp) > + be32_to_cpu(iplb->len)) { + return false; + } + for (i = 0; i < ipib_pv->num_comp; i++) { /* Addr must be 4k aligned */ if (ipib_pv->components[i].addr & ~TARGET_PAGE_MASK) { From a9f1e84c6b8213fb1bfc664b1ffa1a8d975a69b6 Mon Sep 17 00:00:00 2001 From: Joshua Daley Date: Mon, 27 Jul 2026 13:50:50 +0200 Subject: [PATCH 296/860] pc-bios/s390-ccw: fix out-of-bounds read in iso_get_file_size() In the dir_rem[level] == 0 case, level is decremented, then a virtio_read() is issued on sec_loc[level]. If level is -1, then the 4 bytes before the static sec_loc array are read, and the virtio_read() is issued on that garbage block number. Guard the call to virtio_read() against the value of level to prevent this. Fixes: 869648e87eeb ("pc-bios/s390-ccw: El Torito 16-bit boot image size field workaround") Cc: qemu-stable@nongnu.org Signed-off-by: Joshua Daley Reviewed-by: Christian Borntraeger Reviewed-by: Matthew Rosato Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20260727115052.24289-4-borntraeger@linux.ibm.com [farman@linux.ibm.com: Fixed typo on Christian's tag, added qemu-stable] Signed-off-by: Eric Farman --- pc-bios/s390-ccw/bootmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 420ee32eff..ed6e8cbbc7 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -902,7 +902,7 @@ static inline long iso_get_file_size(uint32_t load_rba) if (dir_rem[level] == 0) { /* Nothing remaining */ level--; - if (virtio_read(sec_loc[level], temp)) { + if (level >= 0 && virtio_read(sec_loc[level], temp)) { puts("Failed to read ISO directory"); return -EIO; } From 33909d4ebd5c197626c3142550ee11f4bdb1df48 Mon Sep 17 00:00:00 2001 From: Joshua Daley Date: Mon, 27 Jul 2026 13:50:51 +0200 Subject: [PATCH 297/860] pc-bios/s390-ccw: bounds-check zipl menu entry index before array write menu_get_zipl_boot_index() iterates NUL-separated strings from the zipl stage-2 boot-menu block, passes each to zipl_print_entry() which converts EBCDIC to ASCII and returns atoi(), then writes true into valid_entries[entry]. valid_entries is a MAX_BOOT_ENTRIES element stack array, but entry was never bounds-checked, so a crafted on-disk value could index arbitrarily beyond the array. Fix this in two places: - zipl_print_entry() now validates that the first significant character (after an optional leading space) is a digit. Entries that fail this check return -1 without printing. - menu_get_zipl_boot_index() skips any entry whose index is outside [0, MAX_BOOT_ENTRIES) before writing to valid_entries[]. Fixes: 7385e947fc65 ("pc-bios/s390-ccw: fix non-sequential boot entries (eckd)") Cc: qemu-stable@nongnu.org Signed-off-by: Joshua Daley Reviewed-by: Matthew Rosato Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20260727115052.24289-5-borntraeger@linux.ibm.com [farman@linux.ibm.com: Added qemu-stable] Signed-off-by: Eric Farman --- pc-bios/s390-ccw/menu.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index eeaff78f87..b6a9a56d46 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -176,18 +176,24 @@ int menu_get_boot_index(bool *valid_entries) return boot_index; } -/* Returns the entry number that was printed */ +/* Returns the entry number that was printed, or -1 on invalid entry */ static int zipl_print_entry(const char *data, size_t len) { char buf[len + 2]; + const char *p; ebcdic_to_ascii(data, buf, len); buf[len] = '\n'; buf[len + 1] = '\0'; + p = (buf[0] == ' ') ? buf + 1 : buf; + if (!isdigit((unsigned char)*p)) { + return -1; + } + printf("%s", buf); - return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf); + return atoi(p); } int menu_get_zipl_boot_index(const char *menu_data) @@ -216,6 +222,9 @@ int menu_get_zipl_boot_index(const char *menu_data) entry = zipl_print_entry(menu_data, len); menu_data += len + 1; + if (entry < 0 || entry >= MAX_BOOT_ENTRIES) { + continue; + } valid_entries[entry] = true; if (entry == 0) { From a3856a7ba44ba1b740eee7759b03079e2f5f637b Mon Sep 17 00:00:00 2001 From: Joshua Daley Date: Tue, 28 Jul 2026 15:47:04 +0200 Subject: [PATCH 298/860] pc-bios/s390-ccw: bound zipl menu strlen and replace VLA in zipl_print_entry menu_get_zipl_boot_index() calls strlen() on a pointer into the middle of _s2 with no upper bound, so a stage-2 image whose blocks contain no NUL bytes causes strlen() to walk beyond _s2. The resulting length is then used to size a stack VLA in zipl_print_entry(), risking a stack overflow. Fix by: - Implementing strnlen(), a bounded version of strlen(). s390-ccw uses libc from SLOF, which includes strlen() but does not have an implementation of strnlen(), so we must implement our own. - Adding a menu_data_end parameter to menu_get_zipl_boot_index() and replacing both strlen() calls with strnlen() bounded by the remaining buffer space. The loop guard also checks that the pointer has not reached menu_data_end. The function returns 0 (boot default) if somehow menu_data reaches menu_data_end before printing any entries. - Replacing the VLA char buf[len + 2] in zipl_print_entry() with a fixed ZIPL_ENTRY_MAX + 2 (82-byte) buffer and truncating len before use. - Passing s2_end (_s2 + sizeof(_s2)) as menu_data_end at the one call site in eckd_get_boot_menu_index(), so the bound is exactly the end of the buffer. Fixes: f7178910845a ("s390-ccw: print zipl boot menu") Cc: qemu-stable@nongnu.org Signed-off-by: Joshua Daley Reviewed-by: Matthew Rosato Link: https://lore.kernel.org/qemu-devel/20260728134704.2924005-2-jdaley@linux.ibm.com [farman@linux.ibm.com: Per list, add strnlen rationale to commit message and added cc stable] Signed-off-by: Eric Farman --- pc-bios/s390-ccw/bootmap.c | 4 +++- pc-bios/s390-ccw/helper.h | 10 ++++++++++ pc-bios/s390-ccw/menu.c | 29 +++++++++++++++++++++++------ pc-bios/s390-ccw/s390-ccw.h | 2 +- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index ed6e8cbbc7..81512265ce 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -61,6 +61,7 @@ static uint8_t _s2[MAX_SECTOR_SIZE * 3] __attribute__((__aligned__(PAGE_SIZE))); static void *s2_prev_blk = _s2; static void *s2_cur_blk = _s2 + MAX_SECTOR_SIZE; static void *s2_next_blk = _s2 + MAX_SECTOR_SIZE * 2; +static void *s2_end = _s2 + sizeof(_s2); static inline int verify_boot_info(BootInfo *bip) { @@ -308,7 +309,8 @@ static int eckd_get_boot_menu_index(block_number_t s1b_block_nr) } } - return menu_get_zipl_boot_index(s2_cur_blk + banner_offset); + return menu_get_zipl_boot_index(s2_cur_blk + banner_offset, + s2_end); } prev_block_nr = cur_block_nr; diff --git a/pc-bios/s390-ccw/helper.h b/pc-bios/s390-ccw/helper.h index 8e3dfcb6d6..d9b7da444a 100644 --- a/pc-bios/s390-ccw/helper.h +++ b/pc-bios/s390-ccw/helper.h @@ -45,4 +45,14 @@ static inline void sleep(unsigned int seconds) } } +static inline size_t strnlen(const char *s, size_t maxlen) +{ + size_t len = 0; + + while (len < maxlen && s[len]) { + len++; + } + return len; +} + #endif diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index b6a9a56d46..9b81154b0e 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -16,6 +16,7 @@ #include "s390-ccw.h" #include "sclp.h" #include "s390-time.h" +#include "helper.h" #define KEYCODE_NO_INP '\0' #define KEYCODE_ESCAPE '\033' @@ -26,6 +27,9 @@ #define ZIPL_TIMEOUT_OFFSET 138 #define ZIPL_FLAG_OFFSET 140 +/* Max printable chars for a zipl boot menu entry */ +#define ZIPL_ENTRY_MAX 80 + #define TOD_CLOCK_MILLISECOND 0x3e8000 #define LOW_CORE_EXTERNAL_INT_ADDR 0x86 @@ -179,9 +183,13 @@ int menu_get_boot_index(bool *valid_entries) /* Returns the entry number that was printed, or -1 on invalid entry */ static int zipl_print_entry(const char *data, size_t len) { - char buf[len + 2]; + char buf[ZIPL_ENTRY_MAX + 2]; const char *p; + if (len > ZIPL_ENTRY_MAX) { + len = ZIPL_ENTRY_MAX; + } + ebcdic_to_ascii(data, buf, len); buf[len] = '\n'; buf[len + 1] = '\0'; @@ -196,7 +204,7 @@ static int zipl_print_entry(const char *data, size_t len) return atoi(p); } -int menu_get_zipl_boot_index(const char *menu_data) +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end) { size_t len; int entry; @@ -212,13 +220,22 @@ int menu_get_zipl_boot_index(const char *menu_data) timeout = zipl_timeout * 1000; } - /* Print banner */ + if (menu_data >= menu_data_end) { + return 0; /* Boot default */ + } + + /* Skip banner */ + len = strnlen(menu_data, menu_data_end - menu_data); + menu_data += len + 1; + if (menu_data >= menu_data_end || !(*menu_data)) { + return 0; /* No entries, boot default */ + } + puts("s390-ccw zIPL Boot Menu\n"); - menu_data += strlen(menu_data) + 1; /* Print entries */ - while (*menu_data) { - len = strlen(menu_data); + while (menu_data < menu_data_end && *menu_data) { + len = strnlen(menu_data, menu_data_end - menu_data); entry = zipl_print_entry(menu_data, len); menu_data += len + 1; diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 1e1f71775e..f6030a6071 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -76,7 +76,7 @@ void jump_to_low_kernel(void); /* menu.c */ void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); -int menu_get_zipl_boot_index(const char *menu_data); +int menu_get_zipl_boot_index(const char *menu_data, const char *menu_data_end); bool menu_is_enabled_zipl(void); int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); From c4d9412cdd1c9d1b8b2fcb0e0dbf57ca6286afd7 Mon Sep 17 00:00:00 2001 From: Jared Rossi Date: Tue, 28 Jul 2026 18:30:13 -0400 Subject: [PATCH 299/860] pc-bios/s390-ccw: Fix off-by-one errors with loadparm and boot entries The loadparm may optionally be used to select a boot entry, with the intended range being 0 through 31 inclusive, for a total of 32 entries. Previously, MAX_BOOT_ENTRIES was defined as 31, indicating that it was intended to correspond to the index of the boot entry rather than the count; however, some guards also used MAX_BOOT_ENTRIES as a count of the maximum allowed entries, which resulted in a mismatch between the intended and actual range such that index 31 could never be used in practice. Move the definition of MAX_BOOT_ENTRIES to qipl.h so it is shared and change the value to 32, representing a count of the maximum number of allowed boot entries and allowing the loadparm to accept values 0 through 31 as intended. Update some instances in the netboot code where MAX_BOOT_ENTRIES was used as the max index so that all guards treat MAX_BOOT_ENTRIES as a count across all boot methods. Cc: qemu-stable@nongnu.org Fixes: 806315279d5c ("pc-bios/s390-ccw: Remove panics from ECKD IPL path") Signed-off-by: Jared Rossi Reviewed-by: Matthew Rosato Link: https://lore.kernel.org/qemu-devel/20260728223013.4047042-1-jrossi@linux.ibm.com Signed-off-by: Eric Farman --- include/hw/s390x/ipl/qipl.h | 2 ++ pc-bios/s390-ccw/netmain.c | 11 +++++++---- pc-bios/s390-ccw/s390-ccw.h | 2 -- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h index 8d3c83a80b..b390f2f112 100644 --- a/include/hw/s390x/ipl/qipl.h +++ b/include/hw/s390x/ipl/qipl.h @@ -20,6 +20,8 @@ #define LOADPARM_LEN 8 #define NO_LOADPARM "\0\0\0\0\0\0\0\0" +#define MAX_BOOT_ENTRIES 32 + enum S390IplType { S390_IPL_TYPE_FCP = 0x00, S390_IPL_TYPE_CCW = 0x02, diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 651cedf6ef..791854fce0 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -40,6 +40,9 @@ #define DEFAULT_BOOT_RETRIES 10 #define DEFAULT_TFTP_RETRIES 20 +/* Index 0 is reserved for default alias, start PXE cfg indices at 1 */ +#define PXECFG_MAX (MAX_BOOT_ENTRIES - 1) + extern char _start[]; #define KERNEL_ADDR ((void *)0L) @@ -381,13 +384,13 @@ static int net_select_and_load_kernel(filename_ip_t *fn_ip, static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) { - struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; + struct pl_cfg_entry entries[PXECFG_MAX]; int num_ent, def_ent = 0; num_ent = pxelinux_load_parse_cfg(fn_ip, mac, get_uuid(), DEFAULT_TFTP_RETRIES, cfgbuf, sizeof(cfgbuf), - entries, MAX_BOOT_ENTRIES, &def_ent); + entries, PXECFG_MAX, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } @@ -470,11 +473,11 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) * a magic comment string. */ if (!strncasecmp("# pxelinux", cfgbuf, 10)) { - struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; + struct pl_cfg_entry entries[PXECFG_MAX]; int num_ent, def_ent = 0; num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, - MAX_BOOT_ENTRIES, &def_ent); + PXECFG_MAX, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index f6030a6071..25aac91d45 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -82,8 +82,6 @@ int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); int menu_get_boot_index(bool *valid_entries); -#define MAX_BOOT_ENTRIES 31 - __attribute__ ((__noreturn__)) static inline void panic(const char *string) { From 2c32c273da6d4ecead76441adf96cb685f31df3c Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 14 Jul 2026 22:29:54 +0200 Subject: [PATCH 300/860] target/s390x/tcg: Set STCK/STCKF condition code after the store STORE CLOCK [FAST] to an inaccessible address aborts QEMU: $ qemu-s390x ./stckf ERROR:cc_helper.c:128:cc_calc_addu: assertion failed: (carry_out <= 1) op_stck() sets the condition code with gen_op_movi_cc() before the output operand store, which is deferred to wout_m1_64(). Assigning a constant condition code discards the lazy CC values, so the optimizer drops the writes that produced them. When the store then raises an exception, the instruction is suppressed and s390x_restore_state_to_opc() reinstates the cc_op recorded at the start of STCK[F], but cc_src/cc_dst now hold stale values, so the next condition code evaluation reads garbage. Fix by performing the store manually. The alternative of not discarding in gen_op_movi_cc() keeps the inputs live, but results in less optimal code. Reported-by: Ido Plat Fixes: 434c91a5f4ed ("target-s390: Convert STCK") Cc: qemu-stable@nongnu.org Signed-off-by: Ilya Leoshkevich Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20260714203206.363028-2-iii@linux.ibm.com Signed-off-by: Eric Farman --- target/s390x/tcg/insn-data.h.inc | 4 ++-- target/s390x/tcg/translate.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/target/s390x/tcg/insn-data.h.inc b/target/s390x/tcg/insn-data.h.inc index 0d5392eac5..1ea72248a6 100644 --- a/target/s390x/tcg/insn-data.h.inc +++ b/target/s390x/tcg/insn-data.h.inc @@ -887,8 +887,8 @@ C(0xe32f, STRVG, RXY_a, Z, la2, r1_o, new, m1_64, rev64, 0) /* STORE CLOCK */ - F(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0, IF_IO) - F(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0, IF_IO) + F(0xb205, STCK, S, Z, la2, 0, new, 0, stck, 0, IF_IO) + F(0xb27c, STCKF, S, SCF, la2, 0, new, 0, stck, 0, IF_IO) /* STORE CLOCK EXTENDED */ F(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0, IF_IO) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 82165ac1ec..1b6023168b 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -4108,7 +4108,9 @@ static DisasJumpType op_stap(DisasContext *s, DisasOps *o) static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { gen_helper_stck(o->out, tcg_env); + tcg_gen_qemu_st_i64(o->out, o->addr1, get_mem_index(s), MO_BEUQ); /* ??? We don't implement clock states. */ + /* Set the CC after the store; a suppressed store must preserve it. */ gen_op_movi_cc(s, 0); return DISAS_NEXT; } From e79ea4472691fd7f7b3436dafbabd17c78fc8bf4 Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Tue, 14 Jul 2026 22:29:55 +0200 Subject: [PATCH 301/860] tests/tcg/s390x: Test STCKF condition code on a faulting store Add a small test to prevent regressions. Signed-off-by: Ilya Leoshkevich Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20260714203206.363028-3-iii@linux.ibm.com Signed-off-by: Eric Farman --- tests/tcg/s390x/Makefile.target | 1 + tests/tcg/s390x/stckf.c | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/tcg/s390x/stckf.c diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index 48cba4cdfa..97c0f02190 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -50,6 +50,7 @@ TESTS+=cvb TESTS+=ts TESTS+=ex-smc TESTS+=divide-to-integer +TESTS+=stckf cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread diff --git a/tests/tcg/s390x/stckf.c b/tests/tcg/s390x/stckf.c new file mode 100644 index 0000000000..51c8c9df9f --- /dev/null +++ b/tests/tcg/s390x/stckf.c @@ -0,0 +1,44 @@ +/* + * Test that a faulting STORE CLOCK FAST does not clobber the condition code. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include +#include +#include +#include + +static void handle_sigsegv(int sig, siginfo_t *info, void *ucontext) +{ + mcontext_t *mcontext = &((ucontext_t *)ucontext)->uc_mcontext; + + /* The condition code must be the one set by SLGR, not garbage. */ + _exit(((mcontext->psw.mask >> 44) & 3) == 3 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +int main(void) +{ + struct sigaction act = { + .sa_sigaction = handle_sigsegv, + .sa_flags = SA_SIGINFO, + }; + int err; + + err = sigaction(SIGSEGV, &act, NULL); + assert(err == 0); + + asm volatile( + "lghi %%r1,100\n" + "lghi %%r2,0\n" + "clgr %%r1,%%r2\n" /* CC_OP_LTUGTU_64 */ + /* cc_src=100 is not valid for CC_OP_SUBU */ + "ipm %%r0\n" /* force cc_src to env */ + "lghi %%r3,5\n" + "lghi %%r4,3\n" + "slgr %%r3,%%r4\n" /* CC_OP_SUBU, cc=3 */ + "lghi %%r5,0\n" + "stckf 0(%%r5)\n" /* faults; cc must stay 3 */ + : : : "r0", "r1", "r2", "r3", "r4", "r5", "cc", "memory"); + + return EXIT_FAILURE; +} From 6126cbe2e59d7a9df1128f6072e3480267451187 Mon Sep 17 00:00:00 2001 From: Claudio Imbrenda Date: Wed, 5 Aug 2026 12:37:28 +0200 Subject: [PATCH 302/860] target/s390x: Allow 2G hugepages guest backing Allow mapping guest with 2G hugepages on hosts that support it. Rename kvm_s390_get_hpage_1m() to kvm_s390_get_hpage() to reflect that it is not anymore limited only to 1m hpages. Signed-off-by: Claudio Imbrenda Reviewed-by: Eric Farman Reviewed-by: Hendrik Brueckner Reviewed-by: Matthew Rosato Link: https://lore.kernel.org/qemu-devel/20260805103728.97602-2-imbrenda@linux.ibm.com Signed-off-by: Eric Farman --- target/s390x/diag.c | 2 +- target/s390x/kvm/kvm.c | 40 +++++++++++++++++------------------- target/s390x/kvm/kvm_s390x.h | 2 +- target/s390x/kvm/stubs.c | 2 +- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/target/s390x/diag.c b/target/s390x/diag.c index 80f0958478..46d191b87d 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -185,7 +185,7 @@ out: return false; } - if (kvm_enabled() && kvm_s390_get_hpage_1m()) { + if (kvm_enabled() && kvm_s390_get_hpage()) { error_report("Protected VMs can currently not be backed with " "huge pages"); env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 72031a5714..803b878b4f 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -145,7 +145,7 @@ static int cap_mem_op; static int cap_mem_op_extension; static int cap_s390_irq; static int cap_ri; -static int cap_hpage_1m; +static int cap_hpage; static int cap_vcpu_resets; static int cap_protected; static int cap_zpci_op; @@ -231,7 +231,7 @@ static void kvm_s390_enable_cmma(void) .attr = KVM_S390_VM_MEM_ENABLE_CMMA, }; - if (cap_hpage_1m) { + if (cap_hpage) { warn_report("CMM will not be enabled because it is not " "compatible with huge memory backings."); return; @@ -292,30 +292,28 @@ void kvm_s390_crypto_reset(void) } } -void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) +static bool kvm_s390_pgsize_cap(uint32_t capa, const char *s, Error **errp) { - if (pagesize == 4 * KiB) { - return; + if (kvm_vm_enable_cap(kvm_state, capa, 0)) { + error_setg(errp, "Memory backing with %s pages was specified, " + "but KVM does not support this memory backing", s); + return false; } - - if (pagesize != 1 * MiB) { - error_setg(errp, "Memory backing with 2G pages was specified, " - "but KVM does not support this memory backing"); - return; - } - - if (kvm_vm_enable_cap(kvm_state, KVM_CAP_S390_HPAGE_1M, 0)) { - error_setg(errp, "Memory backing with 1M pages was specified, " - "but KVM does not support this memory backing"); - return; - } - - cap_hpage_1m = 1; + return true; } -int kvm_s390_get_hpage_1m(void) +void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp) { - return cap_hpage_1m; + if (pagesize == MiB) { + cap_hpage = kvm_s390_pgsize_cap(KVM_CAP_S390_HPAGE_1M, "1M", errp); + } else if (pagesize != 4 * KiB) { + cap_hpage = 2 * kvm_s390_pgsize_cap(KVM_CAP_S390_HPAGE_2G, "2G", errp); + } +} + +int kvm_s390_get_hpage(void) +{ + return cap_hpage; } static void ccw_machine_class_foreach(ObjectClass *oc, void *opaque) diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h index 7b1cce3e60..3c4fa0489c 100644 --- a/target/s390x/kvm/kvm_s390x.h +++ b/target/s390x/kvm/kvm_s390x.h @@ -25,7 +25,7 @@ void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code); int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state); void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu); int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu); -int kvm_s390_get_hpage_1m(void); +int kvm_s390_get_hpage(void); int kvm_s390_get_protected_dump(void); int kvm_s390_get_ri(void); int kvm_s390_get_zpci_op(void); diff --git a/target/s390x/kvm/stubs.c b/target/s390x/kvm/stubs.c index 196127baa5..ebf3c83994 100644 --- a/target/s390x/kvm/stubs.c +++ b/target/s390x/kvm/stubs.c @@ -143,7 +143,7 @@ int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu) g_assert_not_reached(); } -int kvm_s390_get_hpage_1m(void) +int kvm_s390_get_hpage(void) { g_assert_not_reached(); } From 84f07211cc5b4fc6a371559bf8a5de4fb068e648 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Aug 2026 10:04:46 -0400 Subject: [PATCH 303/860] Update version for v11.1.0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 63d29a3de9..68d8f15e23 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.0.93 +11.1.0 From 343b1d2d8838c4dc8728baf096933b77c6dbc749 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Aug 2026 11:43:44 -0400 Subject: [PATCH 304/860] Open 11.2 development tree Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 68d8f15e23..cf942bd79b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -11.1.0 +11.1.50 From 0573b1e11a2d746b290c76328b15bf06bf0e36a1 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 30 Jul 2026 10:13:17 +0200 Subject: [PATCH 305/860] MAINTAINERS: add myself to IDE maintainers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Denis V. Lunev CC: Stefan Hajnoczi CC: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260730081325.1816193-2-den@openvz.org> [John Snow has acknowledged this: https://lore.kernel.org/qemu-devel/CAFn=p-abWtYqGtCbH9XTK62oEPK=vLPjy-scwqR+crXvOQ3RrA@mail.gmail.com/ --Stefan] Signed-off-by: Stefan Hajnoczi --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 902db77218..4628793dde 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2160,6 +2160,7 @@ F: docs/specs/edu.rst IDE M: John Snow +M: Denis V. Lunev L: qemu-block@nongnu.org S: Odd Fixes F: include/hw/ide/ From 2fad751ed6b14ef02b8c728b1b88c6c809f767d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Jul 2026 18:41:00 +0200 Subject: [PATCH 306/860] hw/arm/aspeed: Add missing PCI_EXPRESS -> PCIE_PORT Kconfig dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the missing Kconfig dependency on PCIE_PORT to avoid the following runtime error: Type 'aspeed.pcie-root-port' is missing its parent 'pcie-root-port-base' No need to have ASPEED_SOC select PCI_EXPRESS since it is already selected by PCI_EXPRESS_ASPEED. Cc: qemu-stable@nongnu.org Fixes: 2af56518fa9 ("hw/pci-host/aspeed: Add AST2600 PCIe Root Port and make address configurable") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260709164103.37614-2-philmd@oss.qualcomm.com Signed-off-by: Cédric Le Goater --- hw/arm/Kconfig | 1 - hw/pci-host/Kconfig | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 82e0bc2e70..00e2d93803 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -557,7 +557,6 @@ config ASPEED_SOC select MAX31785 select FSI_APB2OPB_ASPEED select AT24C - select PCI_EXPRESS select PCI_EXPRESS_ASPEED config MPS2 diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 8cbb8304a3..bda6e161f1 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -49,6 +49,7 @@ config PCI_I440FX config PCI_EXPRESS_ASPEED bool select PCI_EXPRESS + select PCIE_PORT config PCI_EXPRESS_Q35 bool From 6440880ed371f74b3019462acfb2cd7709372db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Jul 2026 18:41:01 +0200 Subject: [PATCH 307/860] hw/arm/aspeed: Add missing Kconfig dependencies on required components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add various missing Kconfig dependencies of the Aspeed SoCs: - USB EHCI (commit bfdd34f1caa "hw/arm: ast2400/ast2500: Wire up EHCI controllers") - OR-IRQ (commit d831c5fd868 "aspeed/intc: Add AST2700 support") - SDHCI (commit 2bea128c3d0 "hw/sd/aspeed_sdhci: New device" and f25c0ae1079 "aspeed/soc: Add AST2600 support") This fixes the following runtime issues: qemu-system-aarch64: unknown type 'platform-ehci-usb' qemu-system-aarch64: unknown type 'or-irq' qemu-system-aarch64: unknown type 'generic-sdhci' Cc: qemu-stable@nongnu.org Fixes: 2bea128c3d0 ("hw/sd/aspeed_sdhci: New device") Fixes: f25c0ae1079 ("aspeed/soc: Add AST2600 support") Fixes: d831c5fd868 ("aspeed/intc: Add AST2700 support") Fixes: bfdd34f1caa ("hw/arm: ast2400/ast2500: Wire up EHCI controllers") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260709164103.37614-3-philmd@oss.qualcomm.com Signed-off-by: Cédric Le Goater --- hw/arm/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 00e2d93803..6f5b2add66 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -551,6 +551,7 @@ config ASPEED_SOC select TMP105 select TMP421 select EMC141X + select OR_IRQ select UNIMP select LED select PMBUS @@ -558,6 +559,8 @@ config ASPEED_SOC select FSI_APB2OPB_ASPEED select AT24C select PCI_EXPRESS_ASPEED + select USB_EHCI_SYSBUS + select SDHCI config MPS2 bool From 826f9765c0681167eb427f1aafd2fe2c5f3c1f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 9 Jul 2026 18:41:02 +0200 Subject: [PATCH 308/860] hw/arm/aspeed: Add missing Kconfig dependencies on optional components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing Kconfig optional dependencies to avoid the following runtime error: qemu-system-aarch64: -device loader,force-raw=on,addr=0x400000000,file=./u-boot.bin: 'loader' is not a valid device model name qemu-system-aarch64: -device e1000e,netdev=net1,bus=pcie.2: 'e1000e' is not a valid device model name Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260709164103.37614-4-philmd@oss.qualcomm.com Signed-off-by: Cédric Le Goater --- hw/arm/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 6f5b2add66..5869f0f9e8 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -533,7 +533,9 @@ config ASPEED_SOC bool default y depends on TCG && ARM + imply GENERIC_LOADER imply PCI_DEVICES + imply E1000E_PCI_EXPRESS select DS1338 select FTGMAC100 select I2C From 38ed803aebb29ceebcc3e87b0b0d80114b9aa94c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:07 +0000 Subject: [PATCH 309/860] hw/usb/hcd-ehci: Change descriptor addresses to 64-bit with migration compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change internal EHCI descriptor addresses from uint32_t to uint64_t. The following fields are updated: - EHCIPacket::qtdaddr - EHCIQueue::{qhaddr, qtdaddr} - EHCIState::{a_fetch_addr, p_fetch_addr} Update get_dwords() and put_dwords() to take 64-bit addresses and propagate the type change through the descriptor traversal paths. Adjust NLPTR_GET() to operate on 64-bit values: #define NLPTR_GET(x) ((x) & ~0x1fULL) so that link pointer masking works correctly when descriptor addresses exceed 32-bit space. The previous mask (0xffffffe0) implicitly truncated addresses to 32 bits. This patch does not change the on-wire descriptor layout yet. It only removes the internal 32-bit address limit and prepares for later patches that will add full 64-bit QH/qTD/iTD/siTD support. Update the EHCI trace-events prototypes for QH, qTD, iTD, and siTD to use uint64_t for the address argument and print it with PRIx64. This ensures full 64-bit addresses are shown in trace output and improves debugging of queue heads and transfer descriptors. Migration compatibility: To preserve backward migration compatibility, keep the legacy 32-bit fetch address fields (a_fetch_addr_32, p_fetch_addr_32) alongside the new 64-bit fields. Migration format is selected using a machine compat property "x-migrate-fetch-addr-64bit": - Old machine types migrate 32-bit fetch addresses - New machine types migrate full 64-bit fetch addresses This is implemented using VMSTATE_UINT32_TEST() and VMSTATE_UINT64_TEST() so that only the appropriate format is migrated. In pre_save, the 32-bit shadow fields are populated when migrating to old machine types. In post_load, the 32-bit values are restored into the 64-bit fields when loading old migration streams. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/core/machine.c | 2 ++ hw/usb/hcd-ehci.c | 74 +++++++++++++++++++++++++++++++-------------- hw/usb/hcd-ehci.h | 30 +++++++++++++----- hw/usb/trace-events | 24 +++++++-------- 4 files changed, 89 insertions(+), 41 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 73b4d82b4a..eb5e3809d6 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -51,6 +51,8 @@ GlobalProperty hw_compat_11_0[] = { { TYPE_ARM_SMMUV3, "ssidsize", "0" }, { TYPE_ARM_SMMUV3, "oas", "44" }, { "migration", "switchover-ack-legacy", "on" }, + { "sysbus-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, + { "pci-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, }; const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0); diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 28a60e4c1a..1a0c580115 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -72,7 +72,7 @@ typedef enum { } EHCI_STATES; /* macros for accessing fields within next link pointer entry */ -#define NLPTR_GET(x) ((x) & 0xffffffe0) +#define NLPTR_GET(x) ((x) & ~0x1fULL) #define NLPTR_TYPE_GET(x) (((x) >> 1) & 3) #define NLPTR_TBIT(x) ((x) & 1) /* 1=invalid, 0=valid */ @@ -287,7 +287,7 @@ static int ehci_get_state(EHCIState *s, int async) return async ? s->astate : s->pstate; } -static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) +static void ehci_set_fetch_addr(EHCIState *s, int async, uint64_t addr) { if (async) { s->a_fetch_addr = addr; @@ -296,7 +296,7 @@ static void ehci_set_fetch_addr(EHCIState *s, int async, uint32_t addr) } } -static int ehci_get_fetch_addr(EHCIState *s, int async) +static uint64_t ehci_get_fetch_addr(EHCIState *s, int async) { return async ? s->a_fetch_addr : s->p_fetch_addr; } @@ -373,7 +373,7 @@ static inline bool ehci_periodic_enabled(EHCIState *s) } /* Get an array of dwords from main memory */ -static inline int get_dwords(EHCIState *ehci, uint32_t addr, +static inline int get_dwords(EHCIState *ehci, uint64_t addr, uint32_t *buf, int num) { int i; @@ -395,7 +395,7 @@ static inline int get_dwords(EHCIState *ehci, uint32_t addr, } /* Put an array of dwords in to main memory */ -static inline int put_dwords(EHCIState *ehci, uint32_t addr, +static inline int put_dwords(EHCIState *ehci, uint64_t addr, uint32_t *buf, int num) { int i; @@ -549,7 +549,7 @@ static void ehci_free_packet(EHCIPacket *p) /* queue management */ -static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async) +static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint64_t addr, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; EHCIQueue *q; @@ -622,7 +622,7 @@ static void ehci_free_queue(EHCIQueue *q, const char *warn) g_free(q); } -static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr, +static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint64_t addr, int async) { EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues; @@ -1135,7 +1135,7 @@ static void ehci_flush_qh(EHCIQueue *q) { uint32_t *qh = (uint32_t *) &q->qh; uint32_t dwords = sizeof(EHCIqh) >> 2; - uint32_t addr = NLPTR_GET(q->qhaddr); + uint64_t addr = NLPTR_GET(q->qhaddr); put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); } @@ -1406,12 +1406,13 @@ static int ehci_execute(EHCIPacket *p, const char *action) /* 4.7.2 */ static int ehci_process_itd(EHCIState *ehci, EHCIitd *itd, - uint32_t addr) + uint64_t addr) { USBDevice *dev; USBEndpoint *ep; uint32_t i, len, pid, dir, devaddr, endp; - uint32_t pg, off, ptr1, ptr2, max, mult; + uint32_t pg, off, max, mult; + uint64_t ptr1, ptr2; ehci->periodic_sched_active = PERIODIC_ACTIVE; @@ -1528,7 +1529,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) EHCIqh qh; int i = 0; int again = 0; - uint32_t entry = ehci->asynclistaddr; + uint64_t entry = ehci->asynclistaddr; /* set reclamation flag at start event (4.8.6) */ if (async) { @@ -1578,7 +1579,7 @@ out: static int ehci_state_fetchentry(EHCIState *ehci, int async) { int again = 0; - uint32_t entry = ehci_get_fetch_addr(ehci, async); + uint64_t entry = ehci_get_fetch_addr(ehci, async); if (NLPTR_TBIT(entry)) { ehci_set_state(ehci, async, EST_ACTIVE); @@ -1611,8 +1612,8 @@ static int ehci_state_fetchentry(EHCIState *ehci, int async) default: /* TODO: handle FSTN type */ qemu_log_mask(LOG_GUEST_ERROR, - "FETCHENTRY: entry at 0x%x is of type %u " - "which is not supported yet\n", + "FETCHENTRY: entry at %" PRIx64 " is of type %" PRIu64 + " which is not supported yet\n", entry, NLPTR_TYPE_GET(entry)); return -1; } @@ -1623,7 +1624,7 @@ out: static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) { - uint32_t entry; + uint64_t entry; EHCIQueue *q; EHCIqh qh; @@ -1712,7 +1713,7 @@ out: static int ehci_state_fetchitd(EHCIState *ehci, int async) { - uint32_t entry; + uint64_t entry; EHCIitd itd; assert(!async); @@ -1738,7 +1739,7 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) static int ehci_state_fetchsitd(EHCIState *ehci, int async) { - uint32_t entry; + uint64_t entry; EHCIsitd sitd; assert(!async); @@ -1802,7 +1803,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q) EHCIqtd qtd; EHCIPacket *p; int again = 1; - uint32_t addr; + uint64_t addr; addr = NLPTR_GET(q->qtdaddr); if (get_dwords(q->ehci, addr + 8, &qtd.token, 1) < 0) { @@ -1885,7 +1886,7 @@ static int ehci_fill_queue(EHCIPacket *p) USBEndpoint *ep = p->packet.ep; EHCIQueue *q = p->queue; EHCIqtd qtd = p->qtd; - uint32_t qtdaddr; + uint64_t qtdaddr; for (;;) { if (NLPTR_TBIT(qtd.next) != 0) { @@ -2008,7 +2009,8 @@ static int ehci_state_executing(EHCIQueue *q) static int ehci_state_writeback(EHCIQueue *q) { EHCIPacket *p = QTAILQ_FIRST(&q->packets); - uint32_t *qtd, addr; + uint32_t *qtd; + uint64_t addr; int again = 0; /* Write back the QTD from the QH area */ @@ -2414,6 +2416,18 @@ static USBBusOps ehci_bus_ops_standalone = { .wakeup_endpoint = ehci_wakeup_endpoint, }; +static bool ehci_fetch_addr_64_needed(void *opaque, int version_id) +{ + EHCIState *s = opaque; + + return s->migrate_fetch_addr_64bit; +} + +static bool ehci_fetch_addr_32_needed(void *opaque, int version_id) +{ + return !ehci_fetch_addr_64_needed(opaque, version_id); +} + static int usb_ehci_pre_save(void *opaque) { EHCIState *ehci = opaque; @@ -2424,6 +2438,11 @@ static int usb_ehci_pre_save(void *opaque) ehci->last_run_ns -= (ehci->frindex - new_frindex) * UFRAME_TIMER_NS; ehci->frindex = new_frindex; + if (!ehci->migrate_fetch_addr_64bit) { + ehci->migrate_a_fetch_addr = ehci->a_fetch_addr; + ehci->migrate_p_fetch_addr = ehci->p_fetch_addr; + } + return 0; } @@ -2444,6 +2463,11 @@ static int usb_ehci_post_load(void *opaque, int version_id) } } + if (!s->migrate_fetch_addr_64bit) { + s->a_fetch_addr = s->migrate_a_fetch_addr; + s->p_fetch_addr = s->migrate_p_fetch_addr; + } + return 0; } @@ -2504,8 +2528,14 @@ const VMStateDescription vmstate_ehci = { /* schedule state */ VMSTATE_UINT32(astate, EHCIState), VMSTATE_UINT32(pstate, EHCIState), - VMSTATE_UINT32(a_fetch_addr, EHCIState), - VMSTATE_UINT32(p_fetch_addr, EHCIState), + VMSTATE_UINT32_TEST(migrate_a_fetch_addr, EHCIState, + ehci_fetch_addr_32_needed), + VMSTATE_UINT32_TEST(migrate_p_fetch_addr, EHCIState, + ehci_fetch_addr_32_needed), + VMSTATE_UINT64_TEST(a_fetch_addr, EHCIState, + ehci_fetch_addr_64_needed), + VMSTATE_UINT64_TEST(p_fetch_addr, EHCIState, + ehci_fetch_addr_64_needed), VMSTATE_END_OF_LIST() } }; diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index d038ee1e31..cbc32c296d 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -208,7 +208,7 @@ struct EHCIPacket { QTAILQ_ENTRY(EHCIPacket) next; EHCIqtd qtd; /* copy of current QTD (being worked on) */ - uint32_t qtdaddr; /* address QTD read from */ + uint64_t qtdaddr; /* address QTD read from */ USBPacket packet; QEMUSGList sgl; @@ -229,8 +229,8 @@ struct EHCIQueue { * when guest removes an entry (doorbell, handshake sequence) */ EHCIqh qh; /* copy of current QH (being worked on) */ - uint32_t qhaddr; /* address QH read from */ - uint32_t qtdaddr; /* address QTD read from */ + uint64_t qhaddr; /* address QH read from */ + uint64_t qtdaddr; /* address QTD read from */ int last_pid; /* pid of last packet executed */ USBDevice *dev; QTAILQ_HEAD(, EHCIPacket) packets; @@ -256,6 +256,11 @@ struct EHCIState { /* properties */ uint32_t maxframes; + /* + * Controls migration stream compatibility for old machine types. + * Old machine types only transfer 32-bit fetch addresses. + */ + bool migrate_fetch_addr_64bit; /* * EHCI spec version 1.0 Section 2.3 @@ -293,9 +298,18 @@ struct EHCIState { EHCIQueueHead aqueues; EHCIQueueHead pqueues; - /* which address to look at next */ - uint32_t a_fetch_addr; - uint32_t p_fetch_addr; + /* + * which address to look at next + * + * Migration compatibility fields for old machine types that only + * support 32-bit fetch addresses in the migration stream. + * + * New machine types migrate the full 64-bit runtime fetch address. + */ + uint32_t migrate_a_fetch_addr; + uint32_t migrate_p_fetch_addr; + uint64_t a_fetch_addr; + uint64_t p_fetch_addr; USBPacket ipacket; QEMUSGList isgl; @@ -308,7 +322,9 @@ struct EHCIState { }; #define DEFINE_EHCI_COMMON_PROPERTIES(_state) \ - DEFINE_PROP_UINT32("maxframes", _state, ehci.maxframes, 128) + DEFINE_PROP_UINT32("maxframes", _state, ehci.maxframes, 128), \ + DEFINE_PROP_BOOL("x-migrate-fetch-addr-64bit", _state, \ + ehci.migrate_fetch_addr_64bit, true) extern const VMStateDescription vmstate_ehci; diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 0d4318dcf1..8c90688bb3 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -86,15 +86,15 @@ usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio 0x%04 usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio 0x%04x [port %d] = 0x%x (old: 0x%x)" usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d" usb_ehci_state(const char *schedule, const char *state) "%s schedule %s" -usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ 0x%08x: next 0x%08x qtds 0x%08x,0x%08x,0x%08x" -usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ 0x%08x - rl %d, mplen %d, eps %d, ep %d, dev %d" -usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ 0x%08x - c %d, h %d, dtc %d, i %d" +usb_ehci_qh_ptrs(void *q, uint64_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ 0x%" PRIx64 ": next 0x%08x qtds 0x%08x,0x%08x,0x%08x" +usb_ehci_qh_fields(uint64_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ 0x%" PRIx64 " - rl %d, mplen %d, eps %d, ep %d, dev %d" +usb_ehci_qh_bits(uint64_t addr, int c, int h, int dtc, int i) "QH @ 0x%" PRIx64 " - c %d, h %d, dtc %d, i %d" usb_ehci_qh_tbytes(uint32_t tbytes) "updating tbytes to %d" -usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ 0x%08x: next 0x%08x altnext 0x%08x" -usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ 0x%08x - tbytes %d, cpage %d, cerr %d, pid %d" -usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ 0x%08x - ioc %d, active %d, halt %d, babble %d, xacterr %d" -usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ 0x%08x: next 0x%08x - mplen %d, mult %d, ep %d, dev %d" -usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ 0x%08x: next 0x%08x - active %d" +usb_ehci_qtd_ptrs(void *q, uint64_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ 0x%" PRIx64 ": next 0x%08x altnext 0x%08x" +usb_ehci_qtd_fields(uint64_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ 0x%" PRIx64 " - tbytes %d, cpage %d, cerr %d, pid %d" +usb_ehci_qtd_bits(uint64_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ 0x%" PRIx64 " - ioc %d, active %d, halt %d, babble %d, xacterr %d" +usb_ehci_itd(uint64_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ 0x%" PRIx64 ": next 0x%08x - mplen %d, mult %d, ep %d, dev %d" +usb_ehci_sitd(uint64_t addr, uint32_t nxt, uint32_t active) "SITD @ 0x%" PRIx64 ": next 0x%08x - active %d" usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s" usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s" usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d" @@ -104,15 +104,15 @@ usb_ehci_port_resume(uint32_t port) "port #%d" usb_ehci_port_disable(uint32_t port) "port #%d" usb_ehci_queue_action(void *q, const char *action) "q %p: %s" usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s" -usb_ehci_packet_submit(uint32_t qhaddr, uint32_t next, uint32_t qtdaddr, int pid, size_t len, int endp, int status, int actual_length) "qh=0x%x, next=0x%x, qtd=0x%x, pid=0x%x, len=%zd, endp=0x%x, status=%d, actual_length=%d" +usb_ehci_packet_submit(uint64_t qhaddr, uint32_t next, uint64_t qtdaddr, int pid, size_t len, int endp, int status, int actual_length) "qh=0x%" PRIx64 ", next=0x%x, qtd=0x%" PRIx64 ", pid=0x%x, len=%zd, endp=0x%x, status=%d, actual_length=%d" usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x" usb_ehci_guest_bug(const char *reason) "%s" usb_ehci_doorbell_ring(void) "" usb_ehci_doorbell_ack(void) "" usb_ehci_dma_error(void) "" -usb_ehci_execute_complete(uint32_t qhaddr, uint32_t next, uint32_t qtdaddr, int status, int actual_length) "qhaddr=0x%x, next=0x%x, qtdaddr=0x%x, status=%d, actual_length=%d" -usb_ehci_fetchqh_reclaim_done(uint32_t qhaddr) "QH 0x%08x H-bit set, reclamation status reset - done processing" -usb_ehci_fetchqh_dbg(uint32_t qhaddr, uint32_t h, uint32_t halt, uint32_t active, uint32_t next) "QH 0x%08x (h 0x%x halt 0x%x active 0x%x) next 0x%08x" +usb_ehci_execute_complete(uint64_t qhaddr, uint32_t next, uint64_t qtdaddr, int status, int actual_length) "qhaddr=0x%" PRIx64 ", next=0x%x, qtdaddr=0x%" PRIx64 ", status=%d, actual_length=%d" +usb_ehci_fetchqh_reclaim_done(uint64_t qhaddr) "QH 0x%" PRIx64 " H-bit set, reclamation status reset - done processing" +usb_ehci_fetchqh_dbg(uint64_t qhaddr, uint32_t h, uint32_t halt, uint32_t active, uint32_t next) "QH 0x%" PRIx64 " (h 0x%x halt 0x%x active 0x%x) next 0x%08x" usb_ehci_periodic_state_advance(uint32_t frame, uint32_t list, uint32_t entry) "frame=%d, list=0x%x, entry=0x%x" usb_ehci_skipped_uframes(uint64_t skipped_uframes) "skipped %" PRIu64 " uframes" usb_ehci_log(const char *msg) "%s" From 7e6c5727e16fca59af7a681716d25aabc1892f96 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:08 +0000 Subject: [PATCH 310/860] hw/usb/hcd-ehci: Add property to advertise 64-bit addressing capability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new boolean property, "caps-64bit-addr", to control HCCPARAMS[0] (64-bit Addressing Capability). When enabled, the EHCI controller advertises support for 64-bit address memory pointers as defined in the EHCI specification (Table 2-7, HCCPARAMS). This allows software to use the 64-bit data structure formats described in Appendix B. When disabled (default), the controller reports 32-bit addressing capability and uses the standard 32-bit data structures. The EHCI CTRLDSSEGMENT register provides the upper 32 bits [63:32] used to form 64-bit addresses for EHCI control data structures. Per EHCI 1.0 spec section 2.3.5, when the HCCPARAMS 64-bit Addressing Capability bit is zero, CTRLDSSEGMENT is not used: software cannot write it and reads must return zero. Add a capability check in the operational register write handler and reject guest writes to CTRLDSSEGMENT when 64-bit addressing is not enabled. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/usb/hcd-ehci.c | 14 +++++++++++++- hw/usb/hcd-ehci.h | 5 ++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 1a0c580115..e65ea825b0 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1109,6 +1109,15 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, } break; + case CTRLDSSEGMENT: + if (!s->caps_64bit_addr) { + qemu_log_mask(LOG_GUEST_ERROR, + "ehci: write to CTRLDSSEGMENT while " + "64-bit addressing capability is disabled\n"); + return; + } + break; + case ASYNCLISTADDR: if (ehci_async_enabled(s)) { qemu_log_mask(LOG_GUEST_ERROR, @@ -2554,6 +2563,9 @@ void usb_ehci_realize(EHCIState *s, DeviceState *dev, Error **errp) s->maxframes); return; } + if (s->caps_64bit_addr) { + s->caps[0x08] |= BIT(0); + } memory_region_add_subregion(&s->mem, s->capsbase, &s->mem_caps); memory_region_add_subregion(&s->mem, s->opregbase, &s->mem_opreg); @@ -2613,7 +2625,7 @@ void usb_ehci_init(EHCIState *s, DeviceState *dev) s->caps[0x05] = 0x00; /* No companion ports at present */ s->caps[0x06] = 0x00; s->caps[0x07] = 0x00; - s->caps[0x08] = 0x80; /* We can cache whole frame, no 64-bit */ + s->caps[0x08] = 0x80; /* We can cache whole frame */ s->caps[0x0a] = 0x00; s->caps[0x0b] = 0x00; diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index cbc32c296d..f739e6371b 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -261,6 +261,7 @@ struct EHCIState { * Old machine types only transfer 32-bit fetch addresses. */ bool migrate_fetch_addr_64bit; + bool caps_64bit_addr; /* * EHCI spec version 1.0 Section 2.3 @@ -324,7 +325,9 @@ struct EHCIState { #define DEFINE_EHCI_COMMON_PROPERTIES(_state) \ DEFINE_PROP_UINT32("maxframes", _state, ehci.maxframes, 128), \ DEFINE_PROP_BOOL("x-migrate-fetch-addr-64bit", _state, \ - ehci.migrate_fetch_addr_64bit, true) + ehci.migrate_fetch_addr_64bit, true), \ + DEFINE_PROP_BOOL("caps-64bit-addr", _state, \ + ehci.caps_64bit_addr, false) extern const VMStateDescription vmstate_ehci; From 0cb43fdc0b2f2fb7f1c914d734ac32d9b91a5bbe Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:10 +0000 Subject: [PATCH 311/860] hw/usb/hcd-ehci: Implement 64-bit QH descriptor addressing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EHCI supports 64-bit control data structure addressing when the 64-bit Addressing Capability bit in HCCPARAMS is set. In that mode, the CTRLDSSEGMENT register supplies the upper 32 bits which are concatenated with 32-bit link pointer fields to form full 64-bit descriptor addresses (EHCI 1.0, section 2.3.5 and Appendix B). The current implementation assumes 32-bit QH descriptor addresses and directly uses link pointer values without applying the CTRLDSSEGMENT upper dword. Introduce a helper, ehci_get_desc_addr(), to construct full 64-bit descriptor addresses when 64-bit capability is enabled. Update QH traversal paths (async list walk, horizontal QH link, and periodic schedule entry handling) to use the translated 64-bit addresses. EHCI 64-bit buffer pointer fields are defined in Appendix B as split 32-bit low/high parts located at separate offsets, rather than a single contiguous 64-bit field. Therefore, the buffer pointers cannot be represented as uint64_t bufptr[5] without violating the descriptor layout defined by the specification. Introduce ehci_get_buf_addr() to construct full 64-bit buffer addresses from bufptr[] and bufptr_hi[] fields. Use this helper when calculating transfer buffer addresses so that data buffers above 4GB are correctly handled. Also add bufptr_hi[5] to EHCIqh to support 64-bit buffer pointer fields as defined in Appendix B. When 64-bit capability is disabled, descriptor addresses remain 32-bit and existing behaviour is unchanged. Note: Similar split 64-bit buffer pointer handling is required for qTD, iTD and siTD descriptors, which will be addressed in follow-up changes. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/usb/hcd-ehci.c | 72 +++++++++++++++++++++++++++++++++++---------- hw/usb/hcd-ehci.h | 4 +++ hw/usb/trace-events | 2 +- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index e65ea825b0..fd5be7430e 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -96,6 +96,15 @@ typedef enum { *data = val; \ } while (0) +/* + * EHCIqh / EHCIqtd / EHCIitd are sized to always include the extended + * high buffer pointer fields from EHCI 1.0 Appendix B. When 64-bit + * addressing capability is not advertised to the guest, the descriptors + * in guest memory only have the classic 32-bit layout, so DMA transfers + * must not read or write past that boundary. + */ +#define EHCI_QH_DWORDS_32 (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t)) + static const char *ehci_state_names[] = { [EST_INACTIVE] = "INACTIVE", [EST_ACTIVE] = "ACTIVE", @@ -147,6 +156,28 @@ static const char *addr2str(hwaddr addr) return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr); } +static uint64_t ehci_get_buf_addr(const EHCIState *s, uint32_t hi, + uint32_t lo, uint32_t lo_mask) +{ + uint64_t addr = lo & lo_mask; + + if (s->caps_64bit_addr) { + addr = deposit64(addr, 32, 32, hi); + } + + return addr; +} + +static uint64_t ehci_get_desc_addr(const EHCIState *s, uint32_t lo) +{ + return ehci_get_buf_addr(s, s->ctrldssegment, lo, UINT32_MAX); +} + +static uint32_t ehci_qh_dwords(const EHCIState *s) +{ + return s->caps_64bit_addr ? (sizeof(EHCIqh) >> 2) : EHCI_QH_DWORDS_32; +} + static void ehci_trace_usbsts(uint32_t mask, int state) { /* interrupts */ @@ -440,7 +471,7 @@ static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh) (qh->current_qtd != q->qh.current_qtd) || (q->async && qh->next_qtd != q->qh.next_qtd) || (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd, - 7 * sizeof(uint32_t)) != 0) || + EHCI_QH_OVERLAY_COUNT * sizeof(uint32_t)) != 0) || (q->dev != NULL && q->dev->addr != devaddr)) { return false; } else { @@ -487,8 +518,9 @@ static void ehci_writeback_async_complete_packet(EHCIPacket *p) int state; /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */ + memset(&qh, 0, sizeof(qh)); get_dwords(q->ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2); + (uint32_t *) &qh, ehci_qh_dwords(q->ehci)); get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) { @@ -1143,7 +1175,7 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, static void ehci_flush_qh(EHCIQueue *q) { uint32_t *qh = (uint32_t *) &q->qh; - uint32_t dwords = sizeof(EHCIqh) >> 2; + uint32_t dwords = ehci_qh_dwords(q->ehci); uint64_t addr = NLPTR_GET(q->qhaddr); put_dwords(q->ehci, addr + 3 * sizeof(uint32_t), qh + 3, dwords - 3); @@ -1538,7 +1570,9 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) EHCIqh qh; int i = 0; int again = 0; - uint64_t entry = ehci->asynclistaddr; + uint64_t entry = 0; + + entry = ehci_get_desc_addr(ehci, ehci->asynclistaddr); /* set reclamation flag at start event (4.8.6) */ if (async) { @@ -1548,9 +1582,10 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) ehci_queues_rip_unused(ehci, async); /* Find the head of the list (4.9.1.1) */ + memset(&qh, 0, sizeof(qh)); for (i = 0; i < MAX_QH; i++) { if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &qh, - sizeof(EHCIqh) >> 2) < 0) { + ehci_qh_dwords(ehci)) < 0) { return 0; } ehci_trace_qh(NULL, NLPTR_GET(entry), &qh); @@ -1566,8 +1601,8 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async) goto out; } - entry = qh.next; - if (entry == ehci->asynclistaddr) { + entry = ehci_get_desc_addr(ehci, qh.next); + if (entry == ehci_get_desc_addr(ehci, ehci->asynclistaddr)) { break; } } @@ -1651,8 +1686,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) goto out; } + memset(&qh, 0, sizeof(qh)); if (get_dwords(ehci, NLPTR_GET(q->qhaddr), - (uint32_t *) &qh, sizeof(EHCIqh) >> 2) < 0) { + (uint32_t *) &qh, ehci_qh_dwords(ehci)) < 0) { q = NULL; goto out; } @@ -1693,7 +1729,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } if (trace_event_get_state_backends(TRACE_USB_EHCI_FETCHQH_DBG)) { - if (q->qhaddr != q->qh.next) { + if (q->qhaddr != ehci_get_desc_addr(ehci, q->qh.next)) { trace_usb_ehci_fetchqh_dbg(q->qhaddr, q->qh.epchar & QH_EPCHAR_H, q->qh.token & QTD_TOKEN_HALT, @@ -1876,10 +1912,12 @@ static int ehci_state_fetchqtd(EHCIQueue *q) static int ehci_state_horizqh(EHCIQueue *q) { + uint64_t addr; int again = 0; - if (ehci_get_fetch_addr(q->ehci, q->async) != q->qh.next) { - ehci_set_fetch_addr(q->ehci, q->async, q->qh.next); + addr = ehci_get_desc_addr(q->ehci, q->qh.next); + if (ehci_get_fetch_addr(q->ehci, q->async) != addr) { + ehci_set_fetch_addr(q->ehci, q->async, addr); ehci_set_state(q->ehci, q->async, EST_FETCHENTRY); again = 1; } else { @@ -2205,6 +2243,8 @@ static void ehci_advance_periodic_state(EHCIState *ehci) uint32_t entry; uint32_t list; const int async = 0; + uint64_t entry64; + uint64_t list64; /* 4.6 */ @@ -2229,12 +2269,14 @@ static void ehci_advance_periodic_state(EHCIState *ehci) break; } list |= ((ehci->frindex & 0x1ff8) >> 1); - - if (get_dwords(ehci, list, &entry, 1) < 0) { + list64 = ehci_get_desc_addr(ehci, list); + if (get_dwords(ehci, list64, &entry, 1) < 0) { break; } - trace_usb_ehci_periodic_state_advance(ehci->frindex / 8, list, entry); - ehci_set_fetch_addr(ehci, async, entry); + entry64 = ehci_get_desc_addr(ehci, entry); + trace_usb_ehci_periodic_state_advance(ehci->frindex / 8, + list64, entry64); + ehci_set_fetch_addr(ehci, async, entry64); ehci_set_state(ehci, async, EST_FETCHENTRY); ehci_advance_state(ehci, async); ehci_queues_rip_unused(ehci, async); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index f739e6371b..8732264fde 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -141,6 +141,9 @@ typedef struct EHCIqtd { #define QTD_BUFPTR_SH 12 } EHCIqtd; +/* QH overlay: altnext_qtd, token, bufptr[5], bufptr_hi[5] */ +#define EHCI_QH_OVERLAY_COUNT 12 + /* * EHCI spec version 1.0 Section 3.6 */ @@ -194,6 +197,7 @@ typedef struct EHCIqh { #define BUFPTR_FRAMETAG_MASK 0x0000001f #define BUFPTR_SBYTES_MASK 0x00000fe0 #define BUFPTR_SBYTES_SH 5 + uint32_t bufptr_hi[5]; } EHCIqh; enum async_state { diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 8c90688bb3..67249d69c2 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -113,7 +113,7 @@ usb_ehci_dma_error(void) "" usb_ehci_execute_complete(uint64_t qhaddr, uint32_t next, uint64_t qtdaddr, int status, int actual_length) "qhaddr=0x%" PRIx64 ", next=0x%x, qtdaddr=0x%" PRIx64 ", status=%d, actual_length=%d" usb_ehci_fetchqh_reclaim_done(uint64_t qhaddr) "QH 0x%" PRIx64 " H-bit set, reclamation status reset - done processing" usb_ehci_fetchqh_dbg(uint64_t qhaddr, uint32_t h, uint32_t halt, uint32_t active, uint32_t next) "QH 0x%" PRIx64 " (h 0x%x halt 0x%x active 0x%x) next 0x%08x" -usb_ehci_periodic_state_advance(uint32_t frame, uint32_t list, uint32_t entry) "frame=%d, list=0x%x, entry=0x%x" +usb_ehci_periodic_state_advance(uint32_t frame, uint64_t list, uint64_t entry) "frame=%d, list=0x%" PRIx64 ", entry=0x%" PRIx64 usb_ehci_skipped_uframes(uint64_t skipped_uframes) "skipped %" PRIu64 " uframes" usb_ehci_log(const char *msg) "%s" From dea1ed14c931504a56cc767da26eed7fb3ac155b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:11 +0000 Subject: [PATCH 312/860] hw/usb/hcd-ehci: Implement 64-bit qTD descriptor addressing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EHCI supports 64-bit addressing when the 64-bit Addressing Capability bit in HCCPARAMS is set. In that mode, the CTRLDSSEGMENT register provides the upper 32 bits that are concatenated with 32-bit link pointer values to form 64-bit control data structure addresses (EHCI 1.0, section 2.3.5 and Appendix B). qTD link pointers (current_qtd/next_qtd/altnext_qtd and qTD.next) are stored as 32-bit values in the data structures and must be expanded to full 64-bit descriptor addresses when 64-bit mode is enabled. Update the qTD traversal paths to use ehci_get_desc_addr() when following link pointers. Appendix B also defines high dword fields for qTD buffer pointers. Add bufptr_hi[5] to EHCIqtd and extend qTD fetch and QH overlay handling to load and propagate the high buffer pointer fields. When 64-bit capability is disabled, descriptor and buffer addresses remain 32-bit and existing behaviour is unchanged. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/usb/hcd-ehci.c | 33 ++++++++++++++++++++++++--------- hw/usb/hcd-ehci.h | 1 + 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index fd5be7430e..947af86ac4 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -104,6 +104,7 @@ typedef enum { * must not read or write past that boundary. */ #define EHCI_QH_DWORDS_32 (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t)) +#define EHCI_QTD_DWORDS_32 (offsetof(EHCIqtd, bufptr_hi) / sizeof(uint32_t)) static const char *ehci_state_names[] = { [EST_INACTIVE] = "INACTIVE", @@ -178,6 +179,11 @@ static uint32_t ehci_qh_dwords(const EHCIState *s) return s->caps_64bit_addr ? (sizeof(EHCIqh) >> 2) : EHCI_QH_DWORDS_32; } +static uint32_t ehci_qtd_dwords(const EHCIState *s) +{ + return s->caps_64bit_addr ? (sizeof(EHCIqtd) >> 2) : EHCI_QTD_DWORDS_32; +} + static void ehci_trace_usbsts(uint32_t mask, int state) { /* interrupts */ @@ -486,7 +492,8 @@ static bool ehci_verify_qtd(EHCIPacket *p, EHCIqtd *qtd) (p->qtd.next != qtd->next)) || (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd->altnext)) || p->qtd.token != qtd->token || - p->qtd.bufptr[0] != qtd->bufptr[0]) { + p->qtd.bufptr[0] != qtd->bufptr[0] || + p->qtd.bufptr_hi[0] != qtd->bufptr_hi[0]) { return false; } else { return true; @@ -519,10 +526,11 @@ static void ehci_writeback_async_complete_packet(EHCIPacket *p) /* Verify the qh + qtd, like we do when going through fetchqh & fetchqtd */ memset(&qh, 0, sizeof(qh)); + memset(&qtd, 0, sizeof(qtd)); get_dwords(q->ehci, NLPTR_GET(q->qhaddr), (uint32_t *) &qh, ehci_qh_dwords(q->ehci)); get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2); + (uint32_t *) &qtd, ehci_qtd_dwords(q->ehci)); if (!ehci_verify_qh(q, &qh) || !ehci_verify_qtd(p, &qtd)) { p->async = EHCI_ASYNC_INITIALIZED; ehci_free_packet(p); @@ -1215,6 +1223,7 @@ static int ehci_qh_do_overlay(EHCIQueue *q) for (i = 0; i < 5; i++) { q->qh.bufptr[i] = p->qtd.bufptr[i]; + q->qh.bufptr_hi[i] = p->qtd.bufptr_hi[i]; } if (!(q->qh.epchar & QH_EPCHAR_DTC)) { @@ -1248,7 +1257,8 @@ static int ehci_init_transfer(EHCIPacket *p) return -1; } - page = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK; + page = ehci_get_buf_addr(p->queue->ehci, p->qtd.bufptr_hi[cpage], + p->qtd.bufptr[cpage], QTD_BUFPTR_MASK); page += offset; plen = bytes; if (plen > 4096 - offset) { @@ -1744,7 +1754,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && (NLPTR_TBIT(q->qh.current_qtd) == 0) && (q->qh.current_qtd != 0)) { - q->qtdaddr = q->qh.current_qtd; + q->qtdaddr = ehci_get_desc_addr(ehci, q->qh.current_qtd); ehci_set_state(ehci, async, EST_FETCHQTD); } else { @@ -1822,14 +1832,14 @@ static int ehci_state_advqueue(EHCIQueue *q) */ if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) && (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) { - q->qtdaddr = q->qh.altnext_qtd; + q->qtdaddr = ehci_get_desc_addr(q->ehci, q->qh.altnext_qtd); ehci_set_state(q->ehci, q->async, EST_FETCHQTD); /* * next qTD is valid */ } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) { - q->qtdaddr = q->qh.next_qtd; + q->qtdaddr = ehci_get_desc_addr(q->ehci, q->qh.next_qtd); ehci_set_state(q->ehci, q->async, EST_FETCHQTD); /* @@ -1855,10 +1865,14 @@ static int ehci_state_fetchqtd(EHCIQueue *q) return 0; } barrier(); + memset(qtd.bufptr_hi, 0, sizeof(qtd.bufptr_hi)); if (get_dwords(q->ehci, addr + 0, &qtd.next, 1) < 0 || get_dwords(q->ehci, addr + 4, &qtd.altnext, 1) < 0 || get_dwords(q->ehci, addr + 12, qtd.bufptr, - ARRAY_SIZE(qtd.bufptr)) < 0) { + ARRAY_SIZE(qtd.bufptr)) < 0 || + (q->ehci->caps_64bit_addr && + get_dwords(q->ehci, addr + offsetof(EHCIqtd, bufptr_hi), + qtd.bufptr_hi, ARRAY_SIZE(qtd.bufptr_hi)) < 0)) { return 0; } ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); @@ -1939,7 +1953,7 @@ static int ehci_fill_queue(EHCIPacket *p) if (NLPTR_TBIT(qtd.next) != 0) { break; } - qtdaddr = qtd.next; + qtdaddr = ehci_get_desc_addr(q->ehci, qtd.next); /* * Detect circular td lists, Windows creates these, counting on the * active bit going low after execution to make the queue stop. @@ -1949,8 +1963,9 @@ static int ehci_fill_queue(EHCIPacket *p) goto leave; } } + memset(qtd.bufptr_hi, 0, sizeof(qtd.bufptr_hi)); if (get_dwords(q->ehci, NLPTR_GET(qtdaddr), - (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) { + (uint32_t *) &qtd, ehci_qtd_dwords(q->ehci)) < 0) { return -1; } ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd); diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 8732264fde..7fed206906 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -139,6 +139,7 @@ typedef struct EHCIqtd { uint32_t bufptr[5]; /* Standard buffer pointer */ #define QTD_BUFPTR_MASK 0xfffff000 #define QTD_BUFPTR_SH 12 + uint32_t bufptr_hi[5]; } EHCIqtd; /* QH overlay: altnext_qtd, token, bufptr[5], bufptr_hi[5] */ From d027e3c63c60b0c6daf93ccfbd127bb60526ab0d Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:13 +0000 Subject: [PATCH 313/860] hw/usb/hcd-ehci: Implement 64-bit iTD descriptor addressing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EHCI supports 64-bit control data structure addressing when the 64-bit Addressing Capability bit in HCCPARAMS is set. In that mode, the CTRLDSSEGMENT register provides the upper 32 bits that are concatenated with 32-bit link pointer values to form full 64-bit descriptor addresses (EHCI 1.0, section 2.3.5 and Appendix B). iTD link pointers are stored as 32-bit values and must be expanded to full 64-bit descriptor addresses when 64-bit mode is enabled. Update the iTD traversal path to use ehci_get_desc_addr() when following link pointers. Appendix B also defines high dword fields for iTD buffer pointers. Add bufptr_hi[7] to EHCIitd and use ehci_get_buf_addr() to construct full 64-bit buffer addresses from bufptr[] and bufptr_hi[] fields when processing isochronous transfers. This allows buffers above 4GB to be handled correctly. When 64-bit capability is disabled, descriptor and buffer addresses remain 32-bit and existing behaviour is unchanged. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/usb/hcd-ehci.c | 20 +++++++++++++++----- hw/usb/hcd-ehci.h | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 947af86ac4..1a286c32e6 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -105,6 +105,7 @@ typedef enum { */ #define EHCI_QH_DWORDS_32 (offsetof(EHCIqh, bufptr_hi) / sizeof(uint32_t)) #define EHCI_QTD_DWORDS_32 (offsetof(EHCIqtd, bufptr_hi) / sizeof(uint32_t)) +#define EHCI_ITD_DWORDS_32 (offsetof(EHCIitd, bufptr_hi) / sizeof(uint32_t)) static const char *ehci_state_names[] = { [EST_INACTIVE] = "INACTIVE", @@ -184,6 +185,11 @@ static uint32_t ehci_qtd_dwords(const EHCIState *s) return s->caps_64bit_addr ? (sizeof(EHCIqtd) >> 2) : EHCI_QTD_DWORDS_32; } +static uint32_t ehci_itd_dwords(const EHCIState *s) +{ + return s->caps_64bit_addr ? (sizeof(EHCIitd) >> 2) : EHCI_ITD_DWORDS_32; +} + static void ehci_trace_usbsts(uint32_t mask, int state) { /* interrupts */ @@ -1486,7 +1492,8 @@ static int ehci_process_itd(EHCIState *ehci, return -1; } - ptr1 = (itd->bufptr[pg] & ITD_BUFPTR_MASK); + ptr1 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg], + itd->bufptr[pg], ITD_BUFPTR_MASK); qemu_sglist_init(&ehci->isgl, ehci->device, 2, ehci->as); if (off + len > 4096) { /* transfer crosses page border */ @@ -1494,7 +1501,9 @@ static int ehci_process_itd(EHCIState *ehci, qemu_sglist_destroy(&ehci->isgl); return -1; /* avoid page pg + 1 */ } - ptr2 = (itd->bufptr[pg + 1] & ITD_BUFPTR_MASK); + ptr2 = ehci_get_buf_addr(ehci, itd->bufptr_hi[pg + 1], + itd->bufptr[pg + 1], + ITD_BUFPTR_MASK); uint32_t len2 = off + len - 4096; uint32_t len1 = len - len2; qemu_sglist_add(&ehci->isgl, ptr1 + off, len1); @@ -1774,8 +1783,9 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) assert(!async); entry = ehci_get_fetch_addr(ehci, async); + memset(&itd, 0, sizeof(itd)); if (get_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2) < 0) { + ehci_itd_dwords(ehci)) < 0) { return -1; } ehci_trace_itd(ehci, entry, &itd); @@ -1785,8 +1795,8 @@ static int ehci_state_fetchitd(EHCIState *ehci, int async) } put_dwords(ehci, NLPTR_GET(entry), (uint32_t *) &itd, - sizeof(EHCIitd) >> 2); - ehci_set_fetch_addr(ehci, async, itd.next); + ehci_itd_dwords(ehci)); + ehci_set_fetch_addr(ehci, async, ehci_get_desc_addr(ehci, itd.next)); ehci_set_state(ehci, async, EST_FETCHENTRY); return 1; diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 7fed206906..49176f3fd2 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -63,6 +63,7 @@ typedef struct EHCIitd { #define ITD_BUFPTR_MAXPKT_SH 0 #define ITD_BUFPTR_MULT_MASK 0x00000003 #define ITD_BUFPTR_MULT_SH 0 + uint32_t bufptr_hi[7]; } EHCIitd; /* From 11b30c864a2562af9708b6648dbf4d60124687f9 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:14 +0000 Subject: [PATCH 314/860] hw/usb/hcd-ehci: Implement 64-bit siTD descriptor addressing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EHCI supports 64-bit control data structure addressing when the 64-bit Addressing Capability bit in HCCPARAMS is set. In that mode, the CTRLDSSEGMENT register provides the upper 32 bits that are concatenated with 32-bit link pointer values to form full 64-bit descriptor addresses (EHCI 1.0, section 2.3.5 and Appendix B). siTD link pointers are stored as 32-bit values and must be expanded to full 64-bit descriptor addresses when 64-bit mode is enabled. Update the siTD traversal path to use ehci_get_desc_addr() when following link pointers. When 64-bit capability is disabled, descriptor addresses remain 32-bit and existing behaviour is unchanged. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/usb/hcd-ehci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 1a286c32e6..9a80c74972 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1823,7 +1823,7 @@ static int ehci_state_fetchsitd(EHCIState *ehci, int async) warn_report("Skipping active siTD"); } - ehci_set_fetch_addr(ehci, async, sitd.next); + ehci_set_fetch_addr(ehci, async, ehci_get_desc_addr(ehci, sitd.next)); ehci_set_state(ehci, async, EST_FETCHENTRY); return 1; } From 11438bb2b44de3724cde65fc9dfbc423044d0019 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:16 +0000 Subject: [PATCH 315/860] hw/usb/hcd-ehci: Add ctrldssegment-default property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When 64-bit addressing is supported, the Linux EHCI driver programs the segment register to zero. See ehci_run function: https://github.com/torvalds/linux/blob/master/drivers/usb/host/ehci-hcd.c The driver comment also notes that descriptor structures allocated from the DMA pool use segment zero semantics. Descriptor memory is allocated using the DMA API. The platform driver configures a 64-bit DMA mask so memory can be allocated above 4GB. See ehci_platform_probe function: https://github.com/torvalds/linux/blob/master/drivers/usb/host/ehci-platform.c On AST2700 platforms, system DRAM is mapped above 4GB at 0x400000000. As a result, descriptor addresses constructed directly from the guest EHCI registers do not match the actual system address used by the controller when fetching queue heads (QH) and queue element transfer descriptors (qTD). Add a ctrldssegment-default property so platforms can provide a descriptor address offset when constructing descriptor addresses. This allows systems where DRAM resides above 4GB to access EHCI descriptors correctly. The default value is zero, so existing machines are not affected. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/usb/hcd-ehci.c | 1 + hw/usb/hcd-ehci.h | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 9a80c74972..451a918e9f 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1162,6 +1162,7 @@ static void ehci_opreg_write(void *ptr, hwaddr addr, "64-bit addressing capability is disabled\n"); return; } + val |= s->ctrldssegment_default; break; case ASYNCLISTADDR: diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 49176f3fd2..b5ac9c8670 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -268,6 +268,7 @@ struct EHCIState { */ bool migrate_fetch_addr_64bit; bool caps_64bit_addr; + uint32_t ctrldssegment_default; /* * EHCI spec version 1.0 Section 2.3 @@ -333,7 +334,9 @@ struct EHCIState { DEFINE_PROP_BOOL("x-migrate-fetch-addr-64bit", _state, \ ehci.migrate_fetch_addr_64bit, true), \ DEFINE_PROP_BOOL("caps-64bit-addr", _state, \ - ehci.caps_64bit_addr, false) + ehci.caps_64bit_addr, false), \ + DEFINE_PROP_UINT32("ctrldssegment-default", _state, \ + ehci.ctrldssegment_default, 0) extern const VMStateDescription vmstate_ehci; From 8b529eeccbe05b616da5ee86dc980038807c3a06 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:17 +0000 Subject: [PATCH 316/860] hw/arm/aspeed_ast27x0: Set EHCI ctrldssegment-default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On AST2700 platforms, system DRAM is mapped above 4GB with a base address at 0x400000000. The Linux EHCI driver programs the segment register to zero when 64-bit addressing is supported. As a result, descriptor addresses derived from the EHCI registers do not include the DRAM base address. Descriptor memory is allocated through the DMA API with a 64-bit DMA mask, allowing descriptors to reside in DRAM above 4GB. On AST2700, EHCI queue heads (QH) and queue element transfer descriptors (qTD) are therefore placed at addresses starting from 0x400000000. Set the ctrldssegment-default property to "sc->memmap[ASPEED_DEV_SDRAM] >> 32" so the upper 32 bits of descriptor addresses are adjusted accordingly. This allows the emulated EHCI controller to construct correct system addresses when accessing descriptors in DRAM above 4GB. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index dddd7d2106..0b56d2bb67 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -870,6 +870,9 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) /* EHCI */ for (i = 0; i < sc->ehcis_num; i++) { + object_property_set_int(OBJECT(&s->ehci[i]), "ctrldssegment-default", + sc->memmap[ASPEED_DEV_SDRAM] >> 32, + &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } From 39170be7cf5ac55ca079e60d136b9eb5d2a25ff3 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:19 +0000 Subject: [PATCH 317/860] hw/arm/aspeed_ast27x0: Enable 64-bit EHCI DMA addressing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 supports a 64-bit DRAM address space. Therefore, DMA transactions must be capable of accessing 64-bit addresses. Enable the "caps-64bit-addr" property for the EHCI controllers on AST2700 so that USB DMA operations can correctly handle 64-bit memory addresses. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 0b56d2bb67..7c062580f2 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -873,6 +873,8 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(&s->ehci[i]), "ctrldssegment-default", sc->memmap[ASPEED_DEV_SDRAM] >> 32, &error_abort); + object_property_set_bool(OBJECT(&s->ehci[i]), "caps-64bit-addr", true, + &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { return; } From 87c565334014d47ea987bd6a8c0919c03e4c4636 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 13 Jul 2026 03:27:20 +0000 Subject: [PATCH 318/860] tests/functional/aarch64/test_aspeed_ast2700: Add USB EHCI test for AST2700 A1/A2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a functional test to verify USB EHCI support on the AST2700 A2/A1 by attaching a USB keyboard device and checking its enumeration via lsusb. This introduces a helper routine that runs lsusb in the guest and validates that the emulated "QEMU USB Keyboard" is detected. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260713032704.3583103-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/aarch64/test_aspeed_ast2700a1.py | 7 +++++++ tests/functional/aarch64/test_aspeed_ast2700a2.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/tests/functional/aarch64/test_aspeed_ast2700a1.py b/tests/functional/aarch64/test_aspeed_ast2700a1.py index ddddd2d47a..adbbef5cf1 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a1.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a1.py @@ -118,6 +118,11 @@ class AST2x00MachineSDK(QemuSystemTest): 'ip addr show dev eth2', 'inet 10.0.2.15/24') + def do_ast2700_usb_ehci_test(self): + exec_command_and_wait_for_pattern(self, + 'lsusb', + 'QEMU QEMU USB Keyboard') + def start_ast2700_test(self, name, bus_id): num_cpu = 4 load_images_list = [ @@ -161,10 +166,12 @@ class AST2x00MachineSDK(QemuSystemTest): self.archive_extract(self.ASSET_SDK_V1101_AST2700A1) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') + self.vm.add_args('-device', 'usb-kbd,bus=usb-bus.3') self.start_ast2700_test('ast2700-a1-image', 1) self.verify_openbmc_boot_and_login('ast2700-a1') self.do_ast2700_i2c_test(1) self.do_ast2700_pcie_test() + self.do_ast2700_usb_ehci_test() def test_aarch64_ast2700a1_evb_sdk_vbootrom_v11_01(self): self.set_machine('ast2700a1-evb') diff --git a/tests/functional/aarch64/test_aspeed_ast2700a2.py b/tests/functional/aarch64/test_aspeed_ast2700a2.py index 0fe04a6eca..5f9073c897 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a2.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a2.py @@ -121,6 +121,11 @@ class AST2x00MachineSDK(QemuSystemTest): 'ip addr show dev eth2', 'inet 10.0.2.15/24') + def do_ast2700_usb_ehci_test(self): + exec_command_and_wait_for_pattern(self, + 'lsusb', + 'QEMU QEMU USB Keyboard') + def start_ast2700_test(self, name, bus_id): num_cpu = 4 load_images_list = [ @@ -164,10 +169,12 @@ class AST2x00MachineSDK(QemuSystemTest): self.archive_extract(self.ASSET_SDK_V1101_AST2700A2) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') + self.vm.add_args('-device', 'usb-kbd,bus=usb-bus.3') self.start_ast2700_test('ast2700-default-image', 1) self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test(1) self.do_ast2700_pcie_test() + self.do_ast2700_usb_ehci_test() def test_aarch64_ast2700a2_evb_sdk_vbootrom_v11_01(self): self.set_machine('ast2700a2-evb') From 7a79a76286262fa25dcdf7fe585e4a963e67ceed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 14 Jul 2026 14:46:18 +0200 Subject: [PATCH 319/860] tests/qtest: aspeed_smc: Introduce read_page_mem_fn for page read helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This to prepare for fast-read variants. No functional change. Reviewed-by: Bin Meng Link: https://lore.kernel.org/qemu-devel/20260714124621.522948-2-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-smc-utils.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c index c27d09e767..ed125741eb 100644 --- a/tests/qtest/aspeed-smc-utils.c +++ b/tests/qtest/aspeed-smc-utils.c @@ -186,6 +186,9 @@ static void read_page_mem(const AspeedSMCTestData *data, uint32_t addr, } } +typedef void (*read_page_mem_fn)(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page); + static void write_page_mem(const AspeedSMCTestData *data, uint32_t addr, uint32_t write_value) { @@ -327,9 +330,9 @@ void aspeed_smc_test_erase_all(const void *data) flash_reset(test_data); } -void aspeed_smc_test_write_page(const void *data) +static void test_write_page(const AspeedSMCTestData *test_data, + read_page_mem_fn reader) { - const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; uint32_t my_page_addr = test_data->page_addr; uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; @@ -350,13 +353,13 @@ void aspeed_smc_test_write_page(const void *data) spi_ctrl_stop_user(test_data); /* Check what was written */ - read_page(test_data, my_page_addr, page); + reader(test_data, my_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, my_page_addr + i * 4); } /* Check some other page. It should be full of 0xff */ - read_page(test_data, some_page_addr, page); + reader(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } @@ -364,9 +367,14 @@ void aspeed_smc_test_write_page(const void *data) flash_reset(test_data); } -void aspeed_smc_test_read_page_mem(const void *data) +void aspeed_smc_test_write_page(const void *data) +{ + test_write_page(data, read_page); +} + +static void test_read_page_mem(const AspeedSMCTestData *test_data, + read_page_mem_fn reader) { - const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; uint32_t my_page_addr = test_data->page_addr; uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; uint32_t page[FLASH_PAGE_SIZE / 4]; @@ -393,13 +401,13 @@ void aspeed_smc_test_read_page_mem(const void *data) spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); /* Check what was written */ - read_page_mem(test_data, my_page_addr, page); + reader(test_data, my_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, my_page_addr + i * 4); } /* Check some other page. It should be full of 0xff */ - read_page_mem(test_data, some_page_addr, page); + reader(test_data, some_page_addr, page); for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { g_assert_cmphex(page[i], ==, 0xffffffff); } @@ -407,6 +415,11 @@ void aspeed_smc_test_read_page_mem(const void *data) flash_reset(test_data); } +void aspeed_smc_test_read_page_mem(const void *data) +{ + test_read_page_mem(data, read_page_mem); +} + void aspeed_smc_test_write_page_mem(const void *data) { const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; From afd36b9c073276cf57476e2c2b367c6289a62ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 14 Jul 2026 14:46:19 +0200 Subject: [PATCH 320/860] tests/qtest: aspeed_smc: Add fast-read test coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a spi_ctrl_set_fast_read() helper and add read_page_mem_fast_read (CTRL_FREADMODE with dummy byte) and write_page_fast_read (user-mode FAST_READ) tests. While at it, replace the license boilerplate with SPDX identifier. Reviewed-by: Bin Meng Link: https://lore.kernel.org/qemu-devel/20260714124621.522948-3-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-smc-utils.c | 75 ++++++++++++++++++++++++++-------- tests/qtest/aspeed-smc-utils.h | 23 +++-------- tests/qtest/aspeed_smc-test.c | 39 ++++++++++-------- tests/qtest/ast2700-smc-test.c | 4 ++ 4 files changed, 90 insertions(+), 51 deletions(-) diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c index ed125741eb..07e65e0173 100644 --- a/tests/qtest/aspeed-smc-utils.c +++ b/tests/qtest/aspeed-smc-utils.c @@ -4,23 +4,7 @@ * * Copyright (C) 2016 IBM Corp. * - * 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. + * SPDX-License-Identifier: MIT */ #include "qemu/osdep.h" @@ -107,6 +91,23 @@ static void spi_ctrl_setmode(const AspeedSMCTestData *data, uint8_t mode, spi_writel(data, ctrl_reg, ctrl); } +/* Set FREADMODE with a fast read command and 1 dummy byte */ +static void spi_ctrl_set_fast_read(const AspeedSMCTestData *data, uint8_t cmd) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + uint32_t iomode = 0; + + ctrl &= ~(CTRL_USERMODE | (0xff << 16) | + (0x3 << CTRL_DUMMY_LOW_SHIFT) | + (0x1 << CTRL_DUMMY_HIGH_SHIFT) | + CTRL_IO_MODE_MASK); + ctrl |= CTRL_FREADMODE | (cmd << 16) | + (1 << CTRL_DUMMY_LOW_SHIFT) | + iomode; + spi_writel(data, ctrl_reg, ctrl); +} + static void spi_ctrl_start_user(const AspeedSMCTestData *data) { uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; @@ -697,3 +698,43 @@ void aspeed_smc_test_write_page_qpi(const void *data) flash_reset(test_data); } +static void read_page_mem_fast_read(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_set_fast_read(data, FAST_READ); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +void aspeed_smc_test_read_page_mem_fast_read(const void *data) +{ + test_read_page_mem(data, read_page_mem_fast_read); +} + +static void read_page_fast_read(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, FAST_READ); + flash_writel(data, 0, make_be32(addr)); + /* 1 dummy byte for standard SPI fast-read */ + flash_writeb(data, 0, 0x00); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +void aspeed_smc_test_write_page_fast_read(const void *data) +{ + test_write_page(data, read_page_fast_read); +} diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h index e2fd8ff1bd..7504f5a525 100644 --- a/tests/qtest/aspeed-smc-utils.h +++ b/tests/qtest/aspeed-smc-utils.h @@ -4,23 +4,7 @@ * * Copyright (C) 2016 IBM Corp. * - * 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. + * SPDX-License-Identifier: MIT */ #ifndef TESTS_ASPEED_SMC_UTILS_H @@ -44,6 +28,8 @@ #define CTRL_FREADMODE 0x1 #define CTRL_WRITEMODE 0x2 #define CTRL_USERMODE 0x3 +#define CTRL_DUMMY_LOW_SHIFT 6 +#define CTRL_DUMMY_HIGH_SHIFT 14 #define SR_WEL BIT(1) /* @@ -55,6 +41,7 @@ enum { WRDI = 0x4, BULK_ERASE = 0xc7, READ = 0x03, + FAST_READ = 0x0b, PP = 0x02, WRSR = 0x1, WREN = 0x6, @@ -90,5 +77,7 @@ void aspeed_smc_test_status_reg_write_protection(const void *data); void aspeed_smc_test_write_block_protect(const void *data); void aspeed_smc_test_write_block_protect_bottom_bit(const void *data); void aspeed_smc_test_write_page_qpi(const void *data); +void aspeed_smc_test_read_page_mem_fast_read(const void *data); +void aspeed_smc_test_write_page_fast_read(const void *data); #endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 39af1df0ed..4697c493a8 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -4,23 +4,7 @@ * * Copyright (C) 2016 IBM Corp. * - * 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. + * SPDX-License-Identifier: MIT */ #include "qemu/osdep.h" @@ -68,6 +52,15 @@ static void test_palmetto_bmc(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2400/smc/status_reg_write_protection", data, aspeed_smc_test_status_reg_write_protection); + qtest_add_data_func("/ast2400/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2400/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); + /* + * Block protect tests must be run last because the block protect + * state is not cleared by reset_memory() and silently prevents + * subsequent flash writes. + */ qtest_add_data_func("/ast2400/smc/write_block_protect", data, aspeed_smc_test_write_block_protect); qtest_add_data_func("/ast2400/smc/write_block_protect_bottom_bit", @@ -115,6 +108,10 @@ static void test_ast2500_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2500/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast2500/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2500/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); } static void test_ast2600_evb(AspeedSMCTestData *data) @@ -158,6 +155,10 @@ static void test_ast2600_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2600/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast2600/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2600/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); } static void test_ast1030_evb(AspeedSMCTestData *data) @@ -201,6 +202,10 @@ static void test_ast1030_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast1030/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast1030/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast1030/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); } int main(int argc, char **argv) diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c index 33fc47230e..9ad04b574c 100644 --- a/tests/qtest/ast2700-smc-test.c +++ b/tests/qtest/ast2700-smc-test.c @@ -52,6 +52,10 @@ static void test_ast2700_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_status_reg); qtest_add_data_func("/ast2700/smc/write_page_qpi", data, aspeed_smc_test_write_page_qpi); + qtest_add_data_func("/ast2700/smc/read_page_mem_fast_read", + data, aspeed_smc_test_read_page_mem_fast_read); + qtest_add_data_func("/ast2700/smc/write_page_fast_read", + data, aspeed_smc_test_write_page_fast_read); } int main(int argc, char **argv) From da5598227d06112f7d5ef6dcb567173158690565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 14 Jul 2026 14:46:20 +0200 Subject: [PATCH 321/860] tests/qtest: aspeed_smc: Add Dual Output Read (DOR) test coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add read_page_mem_dor (CTRL_FREADMODE with DOR command and dual data IO mode) and write_page_dor (user-mode DOR) tests. Reviewed-by: Bin Meng Link: https://lore.kernel.org/qemu-devel/20260714124621.522948-4-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-smc-utils.c | 45 ++++++++++++++++++++++++++++++++++ tests/qtest/aspeed-smc-utils.h | 4 +++ tests/qtest/aspeed_smc-test.c | 16 ++++++++++++ tests/qtest/ast2700-smc-test.c | 4 +++ 4 files changed, 69 insertions(+) diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c index 07e65e0173..454224ec28 100644 --- a/tests/qtest/aspeed-smc-utils.c +++ b/tests/qtest/aspeed-smc-utils.c @@ -98,6 +98,10 @@ static void spi_ctrl_set_fast_read(const AspeedSMCTestData *data, uint8_t cmd) uint32_t ctrl = spi_readl(data, ctrl_reg); uint32_t iomode = 0; + if (cmd == DOR) { + iomode = CTRL_IO_DUAL_DATA; + } + ctrl &= ~(CTRL_USERMODE | (0xff << 16) | (0x3 << CTRL_DUMMY_LOW_SHIFT) | (0x1 << CTRL_DUMMY_HIGH_SHIFT) | @@ -738,3 +742,44 @@ void aspeed_smc_test_write_page_fast_read(const void *data) { test_write_page(data, read_page_fast_read); } + +static void read_page_mem_dor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_set_fast_read(data, DOR); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +void aspeed_smc_test_read_page_mem_dor(const void *data) +{ + test_read_page_mem(data, read_page_mem_dor); +} + +static void read_page_dor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, DOR); + flash_writel(data, 0, make_be32(addr)); + /* 1 dummy byte for standard SPI DOR */ + flash_writeb(data, 0, 0x00); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +void aspeed_smc_test_write_page_dor(const void *data) +{ + test_write_page(data, read_page_dor); +} diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h index 7504f5a525..04d3c7db5f 100644 --- a/tests/qtest/aspeed-smc-utils.h +++ b/tests/qtest/aspeed-smc-utils.h @@ -28,6 +28,7 @@ #define CTRL_FREADMODE 0x1 #define CTRL_WRITEMODE 0x2 #define CTRL_USERMODE 0x3 +#define CTRL_IO_DUAL_DATA BIT(29) #define CTRL_DUMMY_LOW_SHIFT 6 #define CTRL_DUMMY_HIGH_SHIFT 14 #define SR_WEL BIT(1) @@ -42,6 +43,7 @@ enum { BULK_ERASE = 0xc7, READ = 0x03, FAST_READ = 0x0b, + DOR = 0x3b, PP = 0x02, WRSR = 0x1, WREN = 0x6, @@ -79,5 +81,7 @@ void aspeed_smc_test_write_block_protect_bottom_bit(const void *data); void aspeed_smc_test_write_page_qpi(const void *data); void aspeed_smc_test_read_page_mem_fast_read(const void *data); void aspeed_smc_test_write_page_fast_read(const void *data); +void aspeed_smc_test_read_page_mem_dor(const void *data); +void aspeed_smc_test_write_page_dor(const void *data); #endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 4697c493a8..a67cc5a999 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -56,6 +56,10 @@ static void test_palmetto_bmc(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_fast_read); qtest_add_data_func("/ast2400/smc/write_page_fast_read", data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2400/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2400/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); /* * Block protect tests must be run last because the block protect * state is not cleared by reset_memory() and silently prevents @@ -112,6 +116,10 @@ static void test_ast2500_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_fast_read); qtest_add_data_func("/ast2500/smc/write_page_fast_read", data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2500/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2500/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); } static void test_ast2600_evb(AspeedSMCTestData *data) @@ -159,6 +167,10 @@ static void test_ast2600_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_fast_read); qtest_add_data_func("/ast2600/smc/write_page_fast_read", data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2600/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2600/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); } static void test_ast1030_evb(AspeedSMCTestData *data) @@ -206,6 +218,10 @@ static void test_ast1030_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_fast_read); qtest_add_data_func("/ast1030/smc/write_page_fast_read", data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast1030/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast1030/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); } int main(int argc, char **argv) diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c index 9ad04b574c..bd9d3f7905 100644 --- a/tests/qtest/ast2700-smc-test.c +++ b/tests/qtest/ast2700-smc-test.c @@ -56,6 +56,10 @@ static void test_ast2700_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_fast_read); qtest_add_data_func("/ast2700/smc/write_page_fast_read", data, aspeed_smc_test_write_page_fast_read); + qtest_add_data_func("/ast2700/smc/read_page_mem_dor", + data, aspeed_smc_test_read_page_mem_dor); + qtest_add_data_func("/ast2700/smc/write_page_dor", + data, aspeed_smc_test_write_page_dor); } int main(int argc, char **argv) From a15167a329f7f9bf5a637ae8186ed51e31e5eb63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 14 Jul 2026 14:46:21 +0200 Subject: [PATCH 322/860] tests/qtest: aspeed_smc: Add Quad Output Read (QOR) test coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add read_page_mem_qor (CTRL_FREADMODE with QOR command and quad data IO mode) and write_page_qor (user-mode QOR) tests. Reviewed-by: Bin Meng Link: https://lore.kernel.org/qemu-devel/20260714124621.522948-5-clg@redhat.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-smc-utils.c | 43 ++++++++++++++++++++++++++++++++++ tests/qtest/aspeed-smc-utils.h | 4 ++++ tests/qtest/aspeed_smc-test.c | 16 +++++++++++++ tests/qtest/ast2700-smc-test.c | 4 ++++ 4 files changed, 67 insertions(+) diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c index 454224ec28..6d75a95578 100644 --- a/tests/qtest/aspeed-smc-utils.c +++ b/tests/qtest/aspeed-smc-utils.c @@ -100,6 +100,8 @@ static void spi_ctrl_set_fast_read(const AspeedSMCTestData *data, uint8_t cmd) if (cmd == DOR) { iomode = CTRL_IO_DUAL_DATA; + } else if (cmd == QOR) { + iomode = CTRL_IO_QUAD_DATA; } ctrl &= ~(CTRL_USERMODE | (0xff << 16) | @@ -783,3 +785,44 @@ void aspeed_smc_test_write_page_dor(const void *data) { test_write_page(data, read_page_dor); } + +static void read_page_mem_qor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_set_fast_read(data, QOR); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +void aspeed_smc_test_read_page_mem_qor(const void *data) +{ + test_read_page_mem(data, read_page_mem_qor); +} + +static void read_page_qor(const AspeedSMCTestData *data, + uint32_t addr, uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, QOR); + flash_writel(data, 0, make_be32(addr)); + /* 1 dummy byte for standard SPI QOR */ + flash_writeb(data, 0, 0x00); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +void aspeed_smc_test_write_page_qor(const void *data) +{ + test_write_page(data, read_page_qor); +} diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h index 04d3c7db5f..19c557b822 100644 --- a/tests/qtest/aspeed-smc-utils.h +++ b/tests/qtest/aspeed-smc-utils.h @@ -28,6 +28,7 @@ #define CTRL_FREADMODE 0x1 #define CTRL_WRITEMODE 0x2 #define CTRL_USERMODE 0x3 +#define CTRL_IO_QUAD_DATA BIT(30) #define CTRL_IO_DUAL_DATA BIT(29) #define CTRL_DUMMY_LOW_SHIFT 6 #define CTRL_DUMMY_HIGH_SHIFT 14 @@ -44,6 +45,7 @@ enum { READ = 0x03, FAST_READ = 0x0b, DOR = 0x3b, + QOR = 0x6b, PP = 0x02, WRSR = 0x1, WREN = 0x6, @@ -83,5 +85,7 @@ void aspeed_smc_test_read_page_mem_fast_read(const void *data); void aspeed_smc_test_write_page_fast_read(const void *data); void aspeed_smc_test_read_page_mem_dor(const void *data); void aspeed_smc_test_write_page_dor(const void *data); +void aspeed_smc_test_read_page_mem_qor(const void *data); +void aspeed_smc_test_write_page_qor(const void *data); #endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index a67cc5a999..59c96bd68a 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -60,6 +60,10 @@ static void test_palmetto_bmc(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_dor); qtest_add_data_func("/ast2400/smc/write_page_dor", data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2400/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2400/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); /* * Block protect tests must be run last because the block protect * state is not cleared by reset_memory() and silently prevents @@ -120,6 +124,10 @@ static void test_ast2500_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_dor); qtest_add_data_func("/ast2500/smc/write_page_dor", data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2500/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2500/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); } static void test_ast2600_evb(AspeedSMCTestData *data) @@ -171,6 +179,10 @@ static void test_ast2600_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_dor); qtest_add_data_func("/ast2600/smc/write_page_dor", data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2600/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2600/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); } static void test_ast1030_evb(AspeedSMCTestData *data) @@ -222,6 +234,10 @@ static void test_ast1030_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_dor); qtest_add_data_func("/ast1030/smc/write_page_dor", data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast1030/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast1030/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); } int main(int argc, char **argv) diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c index bd9d3f7905..f85077e04f 100644 --- a/tests/qtest/ast2700-smc-test.c +++ b/tests/qtest/ast2700-smc-test.c @@ -60,6 +60,10 @@ static void test_ast2700_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_dor); qtest_add_data_func("/ast2700/smc/write_page_dor", data, aspeed_smc_test_write_page_dor); + qtest_add_data_func("/ast2700/smc/read_page_mem_qor", + data, aspeed_smc_test_read_page_mem_qor); + qtest_add_data_func("/ast2700/smc/write_page_qor", + data, aspeed_smc_test_write_page_qor); } int main(int argc, char **argv) From ffcbeb1f5a954284519a9ea1942466042b106544 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:01 +0000 Subject: [PATCH 323/860] hw/misc/aspeed_scu: Introduce Aspeed2700SCUState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce Aspeed2700SCUState as an AST2700-specific subclass of AspeedSCUState. Currently, AST1700 and AST2700 reuse the generic AspeedSCUState. However, AST2700 requires SCU functionality that is specific to the platform, particularly for interactions with its coprocessors. Introduce a dedicated Aspeed2700SCUState to provide an extension point for AST2700-specific functionality while keeping the generic AspeedSCUState unchanged. Subsequent patches will migrate AST2700 users to the new subclass and move AST2700-specific code into it. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 8 +++++++- include/hw/misc/aspeed_scu.h | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 5dbf81c0ce..efe1d6315b 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -930,6 +930,11 @@ static void aspeed_ast2700_scu_reset_hold(Object *obj, ResetType type) s->regs[AST2700_HW_STRAP1] = s->hw_strap1; } +static void aspeed_2700_scu_realize(DeviceState *dev, Error **errp) +{ + aspeed_scu_realize(dev, errp); +} + static void aspeed_2700_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -937,6 +942,7 @@ static void aspeed_2700_scu_class_init(ObjectClass *klass, const void *data) AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); dc->desc = "ASPEED 2700 System Control Unit"; + dc->realize = aspeed_2700_scu_realize; rc->phases.hold = aspeed_ast2700_scu_reset_hold; asc->resets = ast2700_a0_resets; asc->calc_hpll = aspeed_2600_scu_calc_hpll; @@ -1161,7 +1167,7 @@ static const TypeInfo aspeed_scu_types[] = { { .name = TYPE_ASPEED_2700_SCU, .parent = TYPE_ASPEED_SCU, - .instance_size = sizeof(AspeedSCUState), + .instance_size = sizeof(Aspeed2700SCUState), .class_init = aspeed_2700_scu_class_init, }, { diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index c30940ab76..904549465f 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -20,6 +20,7 @@ OBJECT_DECLARE_TYPE(AspeedSCUState, AspeedSCUClass, ASPEED_SCU) #define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500" #define TYPE_ASPEED_2600_SCU TYPE_ASPEED_SCU "-ast2600" #define TYPE_ASPEED_2700_SCU TYPE_ASPEED_SCU "-ast2700" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed2700SCUState, ASPEED_2700_SCU) #define TYPE_ASPEED_2700_SCUIO TYPE_ASPEED_SCU "io" "-ast2700" #define TYPE_ASPEED_1030_SCU TYPE_ASPEED_SCU "-ast1030" @@ -41,6 +42,10 @@ struct AspeedSCUState { uint32_t hw_prot_key; }; +struct Aspeed2700SCUState { + AspeedSCUState parent_obj; +}; + #define AST2400_A1_SILICON_REV 0x02010303U #define AST2500_A1_SILICON_REV 0x04010303U #define AST2600_A3_SILICON_REV 0x05030303U From 69ba4d5b967cbc7995ac30775fdf4bc1e7350788 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:02 +0000 Subject: [PATCH 324/860] hw/arm/aspeed: Use Aspeed2700SCUState for AST2700 users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that Aspeed2700SCUState has been introduced, update the AST1700 and AST27x0 SoCs to instantiate the AST2700-specific SCU subclass instead of the generic AspeedSCUState. Also update the AST27x0 FC board to link the SSP/TSP coprocessors to the AST2700 SCU instance. This prepares the AST2700 platform for subsequent patches that move AST2700-specific SCU functionality into the subclass. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 4 ++-- hw/arm/aspeed_ast27x0.c | 16 ++++++++-------- include/hw/arm/aspeed_ast1700.h | 2 +- include/hw/arm/aspeed_soc.h | 1 + 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 7d9fade68d..8d49bb95d6 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -158,7 +158,7 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) 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); + OBJECT(&s->ca35.scu), &error_abort); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { return false; } @@ -190,7 +190,7 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) 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); + OBJECT(&s->ca35.scu), &error_abort); if (!qdev_realize(DEVICE(&s->tsp), NULL, errp)) { return false; } diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 7c062580f2..6365dbd638 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -435,12 +435,12 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "gic", &a->gic, gicv3_class_name()); - object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); - qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", + object_initialize_child(obj, "scu", &a->scu, TYPE_ASPEED_2700_SCU); + qdev_prop_set_uint32(DEVICE(&a->scu), "silicon-rev", sc->silicon_rev); - object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu), + object_property_add_alias(obj, "hw-strap1", OBJECT(&a->scu), "hw-strap1"); - object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), + object_property_add_alias(obj, "hw-prot-key", OBJECT(&a->scu), "hw-prot-key"); object_initialize_child(obj, "scuio", &s->scuio, TYPE_ASPEED_2700_SCUIO); @@ -808,10 +808,10 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sc->memmap[ASPEED_DEV_VBOOTROM], &s->vbootrom); /* SCU */ - if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + if (!sysbus_realize(SYS_BUS_DEVICE(&a->scu), errp)) { return; } - aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&s->scu), 0, + aspeed_mmio_map(s->memory, SYS_BUS_DEVICE(&a->scu), 0, sc->memmap[ASPEED_DEV_SCU]); /* SCU1 */ @@ -934,7 +934,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) AspeedWDTClass *awc = ASPEED_WDT_GET_CLASS(&s->wdt[i]); hwaddr wdt_offset = sc->memmap[ASPEED_DEV_WDT] + i * awc->iosize; - object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&s->scu), + object_property_set_link(OBJECT(&s->wdt[i]), "scu", OBJECT(&a->scu), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { return; @@ -1037,7 +1037,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) aspeed_soc_ast2700_get_irq(s, ASPEED_DEV_EMMC)); /* Timer */ - object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&s->scu), + object_property_set_link(OBJECT(&s->timerctrl), "scu", OBJECT(&a->scu), &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->timerctrl), errp)) { return; diff --git a/include/hw/arm/aspeed_ast1700.h b/include/hw/arm/aspeed_ast1700.h index f7bd4e8650..39c5977cf1 100644 --- a/include/hw/arm/aspeed_ast1700.h +++ b/include/hw/arm/aspeed_ast1700.h @@ -41,7 +41,7 @@ struct AspeedAST1700SoCState { MemoryRegion sram; AspeedSMCState spi; AspeedADCState adc; - AspeedSCUState scu; + Aspeed2700SCUState scu; AspeedGPIOState gpio; AspeedSGPIOState sgpiom[AST1700_SGPIO_NUM]; AspeedI2CState i2c; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 41dc04e293..cd68c7f1ca 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -151,6 +151,7 @@ struct Aspeed27x0SoCState { AspeedINTCState intcioexp[ASPEED_IOEXP_NUM]; GICv3State gic; MemoryRegion dram_empty; + Aspeed2700SCUState scu; }; #define TYPE_ASPEED27X0_SOC "aspeed27x0-soc" From ee3f2ace6fc8c9e079fd49b78ea6f71edb225fdd Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:04 +0000 Subject: [PATCH 325/860] hw/arm/aspeed_ast27x0: Move SCU link into AST27x0 coprocessors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SCU link is only needed by the AST27x0 SSP/TSP coprocessors for their AST2700-specific SCU alias window. Move the link property from the common AspeedCoprocessorState into Aspeed27x0CoprocessorState, so the generic coprocessor model no longer contains an AST2700-specific dependency. Also validate that the SCU link has been provided during device realize before accessing it. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-ssp.c | 20 ++++++++++++++++---- hw/arm/aspeed_ast27x0-tsp.c | 20 ++++++++++++++++---- hw/arm/aspeed_coprocessor_common.c | 2 -- include/hw/arm/aspeed_coprocessor.h | 5 +++-- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c index 68a8ab26f7..28bb59c8c5 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -167,6 +167,12 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->scu) { + error_setg(errp, TYPE_ASPEED27X0SSP_COPROCESSOR + ": 'scu' link is not set"); + return; + } + /* AST27X0 SSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -195,11 +201,11 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) &s->sram_alias); /* SCU */ - memory_region_init_alias(&s->scu_alias, OBJECT(s), "scu.alias", - &s->scu->iomem, 0, - memory_region_size(&s->scu->iomem)); + memory_region_init_alias(&a->scu_alias, OBJECT(a), "scu.alias", + &a->scu->parent_obj.iomem, 0, + memory_region_size(&a->scu->parent_obj.iomem)); memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], - &s->scu_alias); + &a->scu_alias); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { @@ -275,6 +281,11 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_OTP], 0x800); } +static const Property aspeed_27x0_coprocessor_properties[] = { + DEFINE_PROP_LINK("scu", Aspeed27x0CoprocessorState, scu, + TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), +}; + static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const void *data) { @@ -288,6 +299,7 @@ static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, /* Reason: The Aspeed Coprocessor can only be instantiated from a board */ dc->user_creatable = false; dc->realize = aspeed_soc_ast27x0ssp_realize; + device_class_set_props(dc, aspeed_27x0_coprocessor_properties); sc->valid_cpu_types = valid_cpu_types; sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index b8a4f7c91d..f5c4a02161 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -167,6 +167,12 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->scu) { + error_setg(errp, TYPE_ASPEED27X0TSP_COPROCESSOR + ": 'scu' link is not set"); + return; + } + /* AST27X0 TSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -195,11 +201,11 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) &s->sram_alias); /* SCU */ - memory_region_init_alias(&s->scu_alias, OBJECT(s), "scu.alias", - &s->scu->iomem, 0, - memory_region_size(&s->scu->iomem)); + memory_region_init_alias(&a->scu_alias, OBJECT(a), "scu.alias", + &a->scu->parent_obj.iomem, 0, + memory_region_size(&a->scu->parent_obj.iomem)); memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], - &s->scu_alias); + &a->scu_alias); /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { @@ -275,6 +281,11 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_OTP], 0x800); } +static const Property aspeed_27x0_coprocessor_properties[] = { + DEFINE_PROP_LINK("scu", Aspeed27x0CoprocessorState, scu, + TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), +}; + static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const void *data) { @@ -288,6 +299,7 @@ static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, /* Reason: The Aspeed Coprocessor can only be instantiated from a board */ dc->user_creatable = false; dc->realize = aspeed_soc_ast27x0tsp_realize; + device_class_set_props(dc, aspeed_27x0_coprocessor_properties); sc->valid_cpu_types = valid_cpu_types; sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; diff --git a/hw/arm/aspeed_coprocessor_common.c b/hw/arm/aspeed_coprocessor_common.c index a0a4c73d08..43026d2a55 100644 --- a/hw/arm/aspeed_coprocessor_common.c +++ b/hw/arm/aspeed_coprocessor_common.c @@ -27,8 +27,6 @@ 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 *), DEFINE_PROP_LINK("uart", AspeedCoprocessorState, uart, TYPE_SERIAL_MM, SerialMM *), DEFINE_PROP_INT32("uart-dev", AspeedCoprocessorState, uart_dev, 0), diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index ac58a5f424..adfc3c4512 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -20,10 +20,8 @@ struct AspeedCoprocessorState { MemoryRegion *sram; MemoryRegion sram_alias; MemoryRegion uart_alias; - MemoryRegion scu_alias; Clock *sysclk; - AspeedSCUState *scu; AspeedSCUState scuio; AspeedTimerCtrlState timerctrl; SerialMM *uart; @@ -52,6 +50,9 @@ struct Aspeed27x0CoprocessorState { UnimplementedDeviceState otp; ARMv7MState armv7m; + + MemoryRegion scu_alias; + Aspeed2700SCUState *scu; }; #define TYPE_ASPEED27X0SSP_COPROCESSOR "aspeed27x0ssp-coprocessor" From 2fbefe0eb4a39cd3e2ce301602e94e3b2bbe9a9b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:05 +0000 Subject: [PATCH 326/860] hw/misc/aspeed_scu: Add separate reset handler for AST2700 SCUIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a dedicated reset handler for SCUIO. Previously, SCU and SCUIO shared the same reset handler. This no longer fits the AST2700 design, where SCU uses the Aspeed2700SCUState subclass and will handle coprocessor-related control in future changes. Since these controls are defined in SCU (not SCUIO), SCU and SCUIO should not share the same reset logic. This change gives SCUIO its own reset handler and prepares for upcoming SCU-specific functionality. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index efe1d6315b..ca93c3699d 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -1069,6 +1069,16 @@ static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { [AST2700_SCUIO_FREQ_CNT_CTL] = 0x00000080, }; +static void aspeed_ast2700_scuio_reset_hold(Object *obj, ResetType type) +{ + AspeedSCUState *s = ASPEED_SCU(obj); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(obj); + + memcpy(s->regs, asc->resets, asc->nr_regs * 4); + s->regs[AST2700_SILICON_REV] = s->silicon_rev; + s->regs[AST2700_HW_STRAP1] = s->hw_strap1; +} + static void aspeed_2700_scuio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1076,7 +1086,7 @@ static void aspeed_2700_scuio_class_init(ObjectClass *klass, const void *data) AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); dc->desc = "ASPEED 2700 System Control Unit I/O"; - rc->phases.hold = aspeed_ast2700_scu_reset_hold; + rc->phases.hold = aspeed_ast2700_scuio_reset_hold; asc->resets = ast2700_a0_resets_io; asc->calc_hpll = aspeed_2600_scu_calc_hpll; asc->get_apb = aspeed_2700_scuio_get_apb_freq; From 7bb309ed43b06a75f8904011e7385379f4377c67 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:07 +0000 Subject: [PATCH 327/860] hw/arm/aspeed_ast27x0: Pass realized PSP SoC to SSP/TSP initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the realized PSP SoC to the SSP/TSP initialization helpers instead of retrieving it from the MachineState. This makes the dependency explicit, since the SSP and TSP coprocessors use resources owned by the PSP SoC, including the UARTs, SRAM, SCU and SCUIO. The PSP SoC must therefore be realized before the coprocessors are initialized. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 8d49bb95d6..58d0b4d7cd 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -134,11 +134,9 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) return true; } -static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) +static bool ast2700fc_ssp_init(Ast2700FCState *s, AspeedSoCState *psp, + Error **errp) { - Ast2700FCState *s = AST2700FC(machine); - AspeedSoCState *psp = ASPEED_SOC(&s->ca35); - s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); clock_set_hz(s->ssp_sysclk, 200000000ULL); @@ -166,11 +164,9 @@ static bool ast2700fc_ssp_init(MachineState *machine, Error **errp) return true; } -static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) +static bool ast2700fc_tsp_init(Ast2700FCState *s, AspeedSoCState *psp, + Error **errp) { - Ast2700FCState *s = AST2700FC(machine); - AspeedSoCState *psp = ASPEED_SOC(&s->ca35); - s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); clock_set_hz(s->tsp_sysclk, 200000000ULL); @@ -200,9 +196,19 @@ static bool ast2700fc_tsp_init(MachineState *machine, Error **errp) static void ast2700fc_init(MachineState *machine) { + Ast2700FCState *s = AST2700FC(machine); + AspeedSoCState *psp; + ast2700fc_ca35_init(machine, &error_abort); - ast2700fc_ssp_init(machine, &error_abort); - ast2700fc_tsp_init(machine, &error_abort); + + /* + * SSP and TSP use resources owned by the PSP SoC, such as UART, + * SRAM, SCU and SCUIO. Therefore the PSP SoC must be realized + * before the coprocessors are initialized. + */ + psp = ASPEED_SOC(&s->ca35); + ast2700fc_ssp_init(s, psp, &error_abort); + ast2700fc_tsp_init(s, psp, &error_abort); } static void ast2700fc_class_init(ObjectClass *oc, const void *data) From dba6f5c1df462830bf6daa82d7c08c34f76cc164 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:08 +0000 Subject: [PATCH 328/860] hw/arm/ast27x0: Share single SCUIO 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 SCUIO hardware block, memory-mapped at 0x14C02000–0x14C03FFF from the perspective of the main CA35 processor (PSP). The SSP and TSP coprocessors access this same SCUIO block at different addresses: 0x74C02000–0x74C03FFF. Previously, each subsystem (PSP, SSP, and TSP) instantiated its own SCUIO device, resulting in three independent SCUIO instances in the QEMU model. In real hardware, however, only a single SCUIO exists and is shared among all processors. This commit reworks the SCUIO model to correctly reflect the hardware behavior by allowing SSP and TSP to reference the PSP’s SCUIO instance. The following changes are introduced: - Add a scuio property to Aspeed27x0CoprocessorState for linking the coprocessor to the PSP’s SCUIO instance. - Replace per-coprocessor SCUIO instantiation with a shared SCUIO link. - Add "MemoryRegion scuio_alias" to model address remapping for SSP and TSP. - Create SCUIO alias regions in both SSP and TSP coprocessors and map them at 0x74C02000 to mirror the PSP’s SCUIO registers. - Ensure the SCUIO device in PSP is realized before SSP/TSP alias setup. With this change, PSP, SSP, and TSP now share a consistent SCUIO state, matching the single-SCUIO hardware design of AST2700. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 4 ++++ hw/arm/aspeed_ast27x0-ssp.c | 20 +++++++++++++++----- hw/arm/aspeed_ast27x0-tsp.c | 20 +++++++++++++++----- include/hw/arm/aspeed_coprocessor.h | 4 ++-- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 58d0b4d7cd..d14a42ebca 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -157,6 +157,8 @@ static bool ast2700fc_ssp_init(Ast2700FCState *s, AspeedSoCState *psp, OBJECT(&psp->sram), &error_abort); object_property_set_link(OBJECT(&s->ssp), "scu", OBJECT(&s->ca35.scu), &error_abort); + object_property_set_link(OBJECT(&s->ssp), "scuio", + OBJECT(&psp->scuio), &error_abort); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { return false; } @@ -187,6 +189,8 @@ static bool ast2700fc_tsp_init(Ast2700FCState *s, AspeedSoCState *psp, OBJECT(&psp->sram), &error_abort); object_property_set_link(OBJECT(&s->tsp), "scu", OBJECT(&s->ca35.scu), &error_abort); + object_property_set_link(OBJECT(&s->tsp), "scuio", + OBJECT(&psp->scuio), &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 28bb59c8c5..fa492f1797 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -142,8 +142,6 @@ static void aspeed_soc_ast27x0ssp_init(Object *obj) TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "ipc1", &a->ipc[1], TYPE_UNIMPLEMENTED_DEVICE); - object_initialize_child(obj, "scuio", &a->scuio, - TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric0", &a->pric[0], TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric1", &a->pric[1], @@ -173,6 +171,12 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->scuio) { + error_setg(errp, TYPE_ASPEED27X0SSP_COPROCESSOR + ": 'scuio' link is not set"); + return; + } + /* AST27X0 SSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -207,6 +211,13 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], &a->scu_alias); + /* SCUIO */ + memory_region_init_alias(&a->scuio_alias, OBJECT(a), "scuio.alias", + &a->scuio->iomem, 0, + memory_region_size(&a->scuio->iomem)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCUIO], + &a->scuio_alias); + /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { return; @@ -267,9 +278,6 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) 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->memory, SYS_BUS_DEVICE(&a->scuio), - "aspeed.scuio", - sc->memmap[ASPEED_DEV_SCUIO], 0x1000); aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->pric[0]), "aspeed.pric0", sc->memmap[ASPEED_DEV_PRIC0], 0x1000); @@ -284,6 +292,8 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) static const Property aspeed_27x0_coprocessor_properties[] = { DEFINE_PROP_LINK("scu", Aspeed27x0CoprocessorState, scu, TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), + DEFINE_PROP_LINK("scuio", Aspeed27x0CoprocessorState, scuio, + TYPE_ASPEED_SCU, AspeedSCUState *), }; static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index f5c4a02161..3a98bd232c 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -142,8 +142,6 @@ static void aspeed_soc_ast27x0tsp_init(Object *obj) TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "ipc1", &a->ipc[1], TYPE_UNIMPLEMENTED_DEVICE); - object_initialize_child(obj, "scuio", &a->scuio, - TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric0", &a->pric[0], TYPE_UNIMPLEMENTED_DEVICE); object_initialize_child(obj, "pric1", &a->pric[1], @@ -173,6 +171,12 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->scuio) { + error_setg(errp, TYPE_ASPEED27X0TSP_COPROCESSOR + ": 'scuio' link is not set"); + return; + } + /* AST27X0 TSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -207,6 +211,13 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCU], &a->scu_alias); + /* SCUIO */ + memory_region_init_alias(&a->scuio_alias, OBJECT(a), "scuio.alias", + &a->scuio->iomem, 0, + memory_region_size(&a->scuio->iomem)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SCUIO], + &a->scuio_alias); + /* INTC */ if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { return; @@ -267,9 +278,6 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) 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->memory, SYS_BUS_DEVICE(&a->scuio), - "aspeed.scuio", - sc->memmap[ASPEED_DEV_SCUIO], 0x1000); aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&a->pric[0]), "aspeed.pric0", sc->memmap[ASPEED_DEV_PRIC0], 0x1000); @@ -284,6 +292,8 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) static const Property aspeed_27x0_coprocessor_properties[] = { DEFINE_PROP_LINK("scu", Aspeed27x0CoprocessorState, scu, TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), + DEFINE_PROP_LINK("scuio", Aspeed27x0CoprocessorState, scuio, + TYPE_ASPEED_SCU, AspeedSCUState *), }; static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index adfc3c4512..b77ea06e7f 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -22,7 +22,6 @@ struct AspeedCoprocessorState { MemoryRegion uart_alias; Clock *sysclk; - AspeedSCUState scuio; AspeedTimerCtrlState timerctrl; SerialMM *uart; int uart_dev; @@ -45,14 +44,15 @@ struct Aspeed27x0CoprocessorState { AspeedCoprocessorState parent; AspeedINTCState intc[2]; UnimplementedDeviceState ipc[2]; - UnimplementedDeviceState scuio; UnimplementedDeviceState pric[2]; UnimplementedDeviceState otp; ARMv7MState armv7m; MemoryRegion scu_alias; + MemoryRegion scuio_alias; Aspeed2700SCUState *scu; + AspeedSCUState *scuio; }; #define TYPE_ASPEED27X0SSP_COPROCESSOR "aspeed27x0ssp-coprocessor" From ab480be728c2fea79121d1448bda25f1bdbd759c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:09 +0000 Subject: [PATCH 329/860] hw/arm/ast27x0: Share FMC controller with SSP and TSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 provides a single FMC controller shared by the main CA35 processor (PSP) and the SSP/TSP coprocessors. >From the PSP perspective, the FMC controller is memory-mapped at 0x14000000–0x140000FF. The SSP and TSP access the same controller through a different address window at 0x74000000–0x740000FF. This change allows the SSP and TSP SoC models to reference the existing PSP FMC instance instead of creating independent controllers. An MMIO alias is added in the SSP and TSP address spaces to map their FMC access window to the shared FMC device. This ensures consistent FMC state across PSP, SSP, and TSP and matches the AST2700 hardware design. Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 4 ++++ hw/arm/aspeed_ast27x0-ssp.c | 16 ++++++++++++++++ hw/arm/aspeed_ast27x0-tsp.c | 16 ++++++++++++++++ include/hw/arm/aspeed_coprocessor.h | 7 +++++++ 4 files changed, 43 insertions(+) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index d14a42ebca..058cea42ed 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -159,6 +159,8 @@ static bool ast2700fc_ssp_init(Ast2700FCState *s, AspeedSoCState *psp, OBJECT(&s->ca35.scu), &error_abort); object_property_set_link(OBJECT(&s->ssp), "scuio", OBJECT(&psp->scuio), &error_abort); + object_property_set_link(OBJECT(&s->ssp), "fmc", + OBJECT(&psp->fmc), &error_abort); if (!qdev_realize(DEVICE(&s->ssp), NULL, errp)) { return false; } @@ -191,6 +193,8 @@ static bool ast2700fc_tsp_init(Ast2700FCState *s, AspeedSoCState *psp, OBJECT(&s->ca35.scu), &error_abort); object_property_set_link(OBJECT(&s->tsp), "scuio", OBJECT(&psp->scuio), &error_abort); + object_property_set_link(OBJECT(&s->tsp), "fmc", + OBJECT(&psp->fmc), &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 fa492f1797..e03653086c 100644 --- a/hw/arm/aspeed_ast27x0-ssp.c +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -27,6 +27,7 @@ static const hwaddr aspeed_soc_ast27x0ssp_memmap[] = { [ASPEED_DEV_TIMER1] = 0x72C10000, [ASPEED_DEV_UART4] = 0x72C1A000, [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_FMC] = 0x74000000, [ASPEED_DEV_PRIC1] = 0x74100000, [ASPEED_DEV_SCUIO] = 0x74C02000, [ASPEED_DEV_OTP] = 0x74C07000, @@ -177,6 +178,12 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->fmc) { + error_setg(errp, TYPE_ASPEED27X0SSP_COPROCESSOR + ": 'fmc' link is not set"); + return; + } + /* AST27X0 SSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -269,6 +276,13 @@ static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(s->uart), 0, aspeed_soc_ast27x0ssp_get_irq(s, s->uart_dev)); + /* FMC */ + memory_region_init_alias(&a->fmc_alias, OBJECT(a), "fmc.alias", + &a->fmc->mmio, 0, + memory_region_size(&a->fmc->mmio)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_FMC], + &a->fmc_alias); + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", sc->memmap[ASPEED_DEV_TIMER1], 0x200); @@ -294,6 +308,8 @@ static const Property aspeed_27x0_coprocessor_properties[] = { TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), DEFINE_PROP_LINK("scuio", Aspeed27x0CoprocessorState, scuio, TYPE_ASPEED_SCU, AspeedSCUState *), + DEFINE_PROP_LINK("fmc", Aspeed27x0CoprocessorState, fmc, TYPE_ASPEED_SMC, + AspeedSMCState *), }; static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c index 3a98bd232c..39ba062a20 100644 --- a/hw/arm/aspeed_ast27x0-tsp.c +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -27,6 +27,7 @@ static const hwaddr aspeed_soc_ast27x0tsp_memmap[] = { [ASPEED_DEV_TIMER1] = 0x72C10000, [ASPEED_DEV_UART4] = 0x72C1A000, [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_FMC] = 0x74000000, [ASPEED_DEV_PRIC1] = 0x74100000, [ASPEED_DEV_SCUIO] = 0x74C02000, [ASPEED_DEV_OTP] = 0x74C07000, @@ -177,6 +178,12 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) return; } + if (!a->fmc) { + error_setg(errp, TYPE_ASPEED27X0TSP_COPROCESSOR + ": 'fmc' link is not set"); + return; + } + /* AST27X0 TSP Core */ armv7m = DEVICE(&a->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 256); @@ -269,6 +276,13 @@ static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(s->uart), 0, aspeed_soc_ast27x0tsp_get_irq(s, s->uart_dev)); + /* FMC */ + memory_region_init_alias(&a->fmc_alias, OBJECT(a), "fmc.alias", + &a->fmc->mmio, 0, + memory_region_size(&a->fmc->mmio)); + memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_FMC], + &a->fmc_alias); + aspeed_mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->timerctrl), "aspeed.timerctrl", sc->memmap[ASPEED_DEV_TIMER1], 0x200); @@ -294,6 +308,8 @@ static const Property aspeed_27x0_coprocessor_properties[] = { TYPE_ASPEED_2700_SCU, Aspeed2700SCUState *), DEFINE_PROP_LINK("scuio", Aspeed27x0CoprocessorState, scuio, TYPE_ASPEED_SCU, AspeedSCUState *), + DEFINE_PROP_LINK("fmc", Aspeed27x0CoprocessorState, fmc, TYPE_ASPEED_SMC, + AspeedSMCState *), }; static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, diff --git a/include/hw/arm/aspeed_coprocessor.h b/include/hw/arm/aspeed_coprocessor.h index b77ea06e7f..acb8b06457 100644 --- a/include/hw/arm/aspeed_coprocessor.h +++ b/include/hw/arm/aspeed_coprocessor.h @@ -49,10 +49,17 @@ struct Aspeed27x0CoprocessorState { ARMv7MState armv7m; + /* + * SCU, SCUIO and FMC are not owned by this coprocessor: they are + * shared with the main PSP SoC, and only aliased into this + * coprocessor's own address space here. + */ MemoryRegion scu_alias; MemoryRegion scuio_alias; + MemoryRegion fmc_alias; Aspeed2700SCUState *scu; AspeedSCUState *scuio; + AspeedSMCState *fmc; }; #define TYPE_ASPEED27X0SSP_COPROCESSOR "aspeed27x0ssp-coprocessor" From 29139270d8ad70f2c422cc7cf483f465fba42397 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:11 +0000 Subject: [PATCH 330/860] hw/ssi/aspeed_smc: Add Data FIFO-based flash access support for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 supports a Data FIFO mode where flash accesses can be performed directly through Data FIFO MMIO offsets. The Data FIFO start offset increments by one for every 16MB of flash address space, allowing the chip select (CS) to be decoded from the Data FIFO offset. This change adds Data FIFO support to the Aspeed SMC model and introduces a class callback to translate Data FIFO offsets into CS indices. For AST2700, the Data FIFO offset is matched against the segment start address of each CS to determine the target flash device. The SMC register region size (nregs) is also extended dynamically based on the number of supported chip selects to cover all possible Data FIFO regions. This breaks migration compatibility with older QEMU builds for the affected models, even though Aspeed machines are not officially covered by migration compatibility guarantees. Bump version_id to 4 and minimum_version_id to 2 to reflect the incompatible format. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 117 +++++++++++++++++++++++++++++++++--- include/hw/ssi/aspeed_smc.h | 3 +- 2 files changed, 109 insertions(+), 11 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index c8cc6cfa56..bf596f7b2d 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -163,6 +163,9 @@ /* Read Timing Compensation Register */ #define R_TIMINGS (0x94 / 4) +/* Data fifo */ +#define R_DATA_FIFO (0x200 / 4) + /* SPI controller registers and bits (AST2400) */ #define R_SPI_CONF (0x00 / 4) #define SPI_CONF_ENABLE_W0 0 @@ -209,6 +212,7 @@ static const AspeedSegments aspeed_2500_spi2_segments[]; #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2 #define ASPEED_SMC_FEATURE_WDT_CONTROL 0x4 #define ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH 0x08 +#define ASPEED_SMC_FEATURE_DATA_FIFO 0x10 static inline bool aspeed_smc_has_dma(const AspeedSMCClass *asc) { @@ -225,6 +229,11 @@ static inline bool aspeed_smc_has_dma64(const AspeedSMCClass *asc) return !!(asc->features & ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH); } +static inline bool aspeed_smc_has_data_fifo(const AspeedSMCClass *asc) +{ + return !!(asc->features & ASPEED_SMC_FEATURE_DATA_FIFO); +} + #define aspeed_smc_error(fmt, ...) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__) @@ -664,6 +673,7 @@ static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data, { AspeedSMCState *s = ASPEED_SMC(opaque); AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(opaque); + int cs; addr >>= 2; @@ -689,6 +699,18 @@ static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data, trace_aspeed_smc_read(addr << 2, size, s->regs[addr]); *data = s->regs[addr]; + } else if (aspeed_smc_has_data_fifo(asc) && addr >= R_DATA_FIFO) { + cs = asc->data_fifo_offset_to_cs(s, addr << 2); + if (cs >= 0) { + /* + * Data fifo mode only supports SPI user mode. + * The flash address is provided by the SPI command/address cycles, + * the MMIO addr parameter is ignored. + */ + return aspeed_smc_flash_read(&s->flashes[cs], 0, data, size, attrs); + } + aspeed_smc_error("Invalid data fifo offset %" HWADDR_PRIx, addr << 2); + return MEMTX_ERROR; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -1063,6 +1085,19 @@ static MemTxResult aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, } else if (aspeed_smc_has_dma(asc) && aspeed_smc_has_dma64(asc) && addr == R_DMA_DRAM_ADDR_HIGH) { s->regs[addr] = DMA_DRAM_ADDR_HIGH(value); + } else if (aspeed_smc_has_data_fifo(asc) && addr >= R_DATA_FIFO) { + int cs = asc->data_fifo_offset_to_cs(s, addr << 2); + if (cs >= 0) { + /* + * Data fifo mode only supports SPI user mode. + * The flash address is provided by the SPI command/address cycles, + * the MMIO addr parameter is ignored. + */ + return aspeed_smc_flash_write(&s->flashes[cs], 0, data, size, + attrs); + } + aspeed_smc_error("Invalid data fifo offset %" HWADDR_PRIx, addr << 2); + return MEMTX_ERROR; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -1183,8 +1218,8 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_smc = { .name = "aspeed.smc", - .version_id = 3, - .minimum_version_id = 1, + .version_id = 4, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), VMSTATE_UNUSED_V(2, 2), /* was snoop_index/snoop_dummies */ @@ -1808,6 +1843,39 @@ static void aspeed_2700_smc_reg_to_segment(const AspeedSMCState *s, } } +/* + * Convert a data fifo offset to a chip select (CS). + * + * Data fifo access starts at 0x200. The data fifo offset index is + * calculated by subtracting the data fifo base offset from the MMIO address. + * + * The data fifo offset index increments by 1 for every 16MB of flash address + * space. Each offset step therefore represents a 16MB address decode range. + * + * The CS is determined by matching the data fifo offset index against the + * segment start address of each CS. + * + * Returns the CS index on success, or -1 if the offset is invalid. + */ +static int aspeed_2700_smc_data_fifo_offset_to_cs(const AspeedSMCState *s, + uint32_t offset) +{ + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s); + uint32_t start_offset; + uint32_t fifo_offset; + int i; + + for (i = 0; i < asc->cs_num_max; i++) { + start_offset = (s->regs[R_SEG_ADDR0 + i] & 0x0000ffff) << 16; + fifo_offset = start_offset / 0x1000000; + if (fifo_offset == offset - (R_DATA_FIFO << 2)) { + return i; + } + } + + return -1; +} + static const uint32_t aspeed_2700_fmc_resets[ASPEED_SMC_R_MAX] = { [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 | CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1), @@ -1842,6 +1910,27 @@ static const AspeedSegments aspeed_2700_fmc_segments[] = { { 0x0, 0 }, /* disabled */ }; +/* + * AST2700 supports data fifo mode with a base data fifo start offset of 0x200. + * + * The data fifo start offset increments by 1 for every 16MB of flash address + * space. Each offset step therefore represents a 16MB address decode range. + * + * Assuming each chip select (CS) can use the maximum flash size of 256MB: + * 256MB / 16MB = 0x10 offset steps per CS. + * + * Data fifo start offset for CSn: + * 0x200 + (n * 0x10) + * + * Examples: + * CS0: 0x200 + * CS1: 0x210 + * CS2: 0x220 + * CS3: 0x230 + * + * asc->nregs should be set to: 0x200 + (asc->cs_num_max * 0x10) + * to cover all possible data fifo regions. + */ static void aspeed_2700_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1861,14 +1950,16 @@ static void aspeed_2700_fmc_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x100000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x2FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } @@ -1896,14 +1987,16 @@ static void aspeed_2700_spi0_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x180000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x2FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } @@ -1930,14 +2023,16 @@ static void aspeed_2700_spi1_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x200000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x2FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } @@ -1964,14 +2059,16 @@ static void aspeed_2700_spi2_class_init(ObjectClass *klass, const void *data) asc->flash_window_base = 0x280000000; asc->flash_window_size = 1 * GiB; asc->features = ASPEED_SMC_FEATURE_DMA | - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH; + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH | + ASPEED_SMC_FEATURE_DATA_FIFO; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0xFFFFFFFC; asc->dma_start_length = 1; - asc->nregs = ASPEED_SMC_R_MAX; + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2; asc->segment_to_reg = aspeed_2700_smc_segment_to_reg; asc->reg_to_segment = aspeed_2700_smc_reg_to_segment; asc->dma_ctrl = aspeed_2600_smc_dma_ctrl; + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs; asc->reg_ops = &aspeed_2700_smc_flash_ops; } diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index a273365689..5f391fc24e 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -47,7 +47,7 @@ struct AspeedSMCFlash { #define TYPE_ASPEED_SMC "aspeed.smc" OBJECT_DECLARE_TYPE(AspeedSMCState, AspeedSMCClass, ASPEED_SMC) -#define ASPEED_SMC_R_MAX (0x100 / 4) +#define ASPEED_SMC_R_MAX (0x300 / 4) #define ASPEED_SMC_CS_MAX 5 struct AspeedSMCState { @@ -114,6 +114,7 @@ struct AspeedSMCClass { AspeedSegments *seg); void (*dma_ctrl)(AspeedSMCState *s, uint32_t value); int (*addr_width)(const AspeedSMCState *s); + int (*data_fifo_offset_to_cs)(const AspeedSMCState *s, uint32_t offset); const MemoryRegionOps *reg_ops; }; From 828872aa980ce747e835a4182f9958b9e6e7554c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 17 Jul 2026 08:46:12 +0000 Subject: [PATCH 331/860] tests/qtest/ast2700-smc-test: Add Data FIFO mode test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two qtest cases exercising the new AST2700 Data FIFO-based flash access path (R_DATA_FIFO at spi_base + 0x200). Write_page_datafifo sends the page-program command and data through the FIFO port, then verifies the result via the regular read path. Read_page_datafifo writes a page the regular way, then reads it back through the FIFO port, so both directions are checked independently. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-smc-utils.c | 102 +++++++++++++++++++++++++++++++++ tests/qtest/aspeed-smc-utils.h | 4 ++ tests/qtest/ast2700-smc-test.c | 4 ++ 3 files changed, 110 insertions(+) diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c index 6d75a95578..146332240d 100644 --- a/tests/qtest/aspeed-smc-utils.c +++ b/tests/qtest/aspeed-smc-utils.c @@ -57,6 +57,28 @@ static inline uint32_t flash_readl(const AspeedSMCTestData *data, return qtest_readl(data->s, data->flash_base + offset); } +/* + * Data FIFO port, in spi_base's register bank (not flash_base). Accesses + * through the FIFO require the complete user-mode transaction (opcode, + * address, and data). Assumes CS0, whose FIFO slot is at R_DATA_FIFO. + */ +static inline void datafifo_writeb(const AspeedSMCTestData *data, + uint8_t value) +{ + qtest_writeb(data->s, data->spi_base + R_DATA_FIFO, value); +} + +static inline void datafifo_writel(const AspeedSMCTestData *data, + uint32_t value) +{ + spi_writel(data, R_DATA_FIFO, value); +} + +static inline uint32_t datafifo_readl(const AspeedSMCTestData *data) +{ + return spi_readl(data, R_DATA_FIFO); +} + static void spi_conf(const AspeedSMCTestData *data, uint32_t value) { uint32_t conf = spi_readl(data, R_CONF); @@ -826,3 +848,83 @@ void aspeed_smc_test_write_page_qor(const void *data) { test_write_page(data, read_page_qor); } + +void aspeed_smc_test_write_page_datafifo(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Send the complete user-mode transaction (opcode, address, data) + * through the Data FIFO port. + */ + spi_ctrl_start_user(test_data); + datafifo_writeb(test_data, EN_4BYTE_ADDR); + datafifo_writeb(test_data, WREN); + datafifo_writeb(test_data, PP); + datafifo_writel(test_data, make_be32(my_page_addr)); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + datafifo_writel(test_data, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check what was written, using the regular read path */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_page_datafifo(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* Write the page the regular way */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* + * Read it back through the data FIFO port, again sending the whole + * transaction (opcode, address, data) through it. + */ + spi_ctrl_start_user(test_data); + datafifo_writeb(test_data, EN_4BYTE_ADDR); + datafifo_writeb(test_data, READ); + datafifo_writel(test_data, make_be32(my_page_addr)); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(datafifo_readl(test_data)); + } + spi_ctrl_stop_user(test_data); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + flash_reset(test_data); +} diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h index 19c557b822..e4f538e579 100644 --- a/tests/qtest/aspeed-smc-utils.h +++ b/tests/qtest/aspeed-smc-utils.h @@ -33,6 +33,8 @@ #define CTRL_DUMMY_LOW_SHIFT 6 #define CTRL_DUMMY_HIGH_SHIFT 14 #define SR_WEL BIT(1) +/* Data fifo */ +#define R_DATA_FIFO 0x200 /* * Flash commands @@ -87,5 +89,7 @@ void aspeed_smc_test_read_page_mem_dor(const void *data); void aspeed_smc_test_write_page_dor(const void *data); void aspeed_smc_test_read_page_mem_qor(const void *data); void aspeed_smc_test_write_page_qor(const void *data); +void aspeed_smc_test_write_page_datafifo(const void *data); +void aspeed_smc_test_read_page_datafifo(const void *data); #endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c index f85077e04f..925dbcfaaf 100644 --- a/tests/qtest/ast2700-smc-test.c +++ b/tests/qtest/ast2700-smc-test.c @@ -64,6 +64,10 @@ static void test_ast2700_evb(AspeedSMCTestData *data) data, aspeed_smc_test_read_page_mem_qor); qtest_add_data_func("/ast2700/smc/write_page_qor", data, aspeed_smc_test_write_page_qor); + qtest_add_data_func("/ast2700/smc/write_page_datafifo", + data, aspeed_smc_test_write_page_datafifo); + qtest_add_data_func("/ast2700/smc/read_page_datafifo", + data, aspeed_smc_test_read_page_datafifo); } int main(int argc, char **argv) From 325d1c9344484d6653db3d764facf2a5aad96c3d Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Jul 2026 11:15:54 +0200 Subject: [PATCH 332/860] hw/sensor: adc128d818: add 12-bit 8-channel ADC device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADC128D818 is a TI 12-bit, 8-channel I2C ADC used on several OpenBMC platforms for voltage and temperature monitoring. Implement the device with: - four operating modes - 12-bit voltage conversion from QOM inputs - 9-bit temperature conversion from milli-degree Celsius QOM inputs - switchable internal or external voltage reference - per-channel high/low limit registers - interrupt support - software reset - one-shot conversion support in shutdown mode Reviewed-by: Alexander Hansen Tested-by: Alexander Hansen Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-2-emmanuel.blot@free.fr Signed-off-by: Cédric Le Goater --- hw/sensor/Kconfig | 4 + hw/sensor/adc128d818.c | 696 +++++++++++++++++++++++++++++++++ hw/sensor/meson.build | 1 + hw/sensor/trace-events | 8 + include/hw/sensor/adc128d818.h | 14 + 5 files changed, 723 insertions(+) create mode 100644 hw/sensor/adc128d818.c create mode 100644 include/hw/sensor/adc128d818.h diff --git a/hw/sensor/Kconfig b/hw/sensor/Kconfig index bc6331b4ab..b459ac2240 100644 --- a/hw/sensor/Kconfig +++ b/hw/sensor/Kconfig @@ -1,3 +1,7 @@ +config ADC128D818 + bool + depends on I2C + config TMP105 bool depends on I2C diff --git a/hw/sensor/adc128d818.c b/hw/sensor/adc128d818.c new file mode 100644 index 0000000000..c65508cba1 --- /dev/null +++ b/hw/sensor/adc128d818.c @@ -0,0 +1,696 @@ +/* + * Texas Instruments ADC128D818 12-bit 8-channel ADC with I2C interface + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qom/object.h" +#include "hw/sensor/adc128d818.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "hw/i2c/i2c.h" +#include "migration/vmstate.h" +#include "trace.h" + + +/* Register addresses */ +#define REG_CONFIG 0x00 +#define REG_INT_STATUS 0x01 +#define REG_INT_MASK 0x03 +#define REG_CONV_RATE 0x07 +#define REG_CH_DISABLE 0x08 +#define REG_ONE_SHOT 0x09 +#define REG_DEEP_SHUTDOWN 0x0a +#define REG_ADV_CONFIG 0x0b +#define REG_BUSY_STATUS 0x0c + +/* Channel Reading Registers (16-bit, read-only) */ +#define REG_CH_READING_BASE 0x20 +#define REG_CH_READING_LAST 0x27 + +/* Limit Registers (8-bit, read/write) */ +#define REG_LIMIT_BASE 0x2a +#define REG_LIMIT_LAST 0x39 + +/* ID Registers (read-only) */ +#define REG_MANUFACTURER_ID 0x3e +#define REG_REVISION_ID 0x3f + +/* Configuration Register (0x00) bitfields */ +#define CONFIG_START BIT(0) +#define CONFIG_INT_ENABLE BIT(1) +#define CONFIG_INT_CLEAR BIT(3) +#define CONFIG_INITIALIZATION BIT(7) +#define CONFIG_WR_MASK \ + (CONFIG_START | CONFIG_INT_ENABLE | CONFIG_INT_CLEAR) + +/* Advanced Configuration Register (0x0B) bitfields */ +#define ADV_CONFIG_EXT_REF_EN BIT(0) +#define ADV_CONFIG_MODE_SHIFT 1 +#define ADV_CONFIG_MODE_MASK (0x3 << ADV_CONFIG_MODE_SHIFT) +#define ADV_CONFIG_WR_MASK \ + (ADV_CONFIG_EXT_REF_EN | ADV_CONFIG_MODE_MASK) + +/* Busy Status Register (0x0C) bitfields */ +#define BUSY_STATUS_NOT_READY BIT(1) + +/* Conversion Rate Register (0x07) bitfields */ +#define CONV_RATE_MASK 0x01 + +/* Deep Shutdown Register (0x0A) bitfields */ +#define DEEP_SHUTDOWN_EN 0x01 + +/* Device constants */ +#define ADC128D818_NUM_CHANNELS 8 +#define ADC128D818_NUM_REGS 0x40 + +#define ADC128D818_INTERNAL_VREF_MV 2560 +#define ADC128D818_MAX_VDD_MV 5500 +#define ADC128D818_MANUFACTURER_ID_VAL 0x01 +#define ADC128D818_REVISION_ID_VAL 0x09 + +/* ADC resolution */ +#define ADC128D818_ADC_RESOLUTION 4096 +#define ADC128D818_ADC_MAX 4095 + +/* Temperature: 0.5 deg C per LSb = 500 milli-degrees per LSb */ +#define ADC128D818_TEMP_LSB_MC 500 +#define ADC128D818_TEMP_RAW_MIN (-256) +#define ADC128D818_TEMP_RAW_MAX 255 + + +OBJECT_DECLARE_SIMPLE_TYPE(ADC128D818State, ADC128D818) + +struct ADC128D818State { + I2CSlave parent_obj; + + qemu_irq irq; + + uint8_t len; + uint8_t pointer; + uint8_t rx_byte; + + uint8_t regs[ADC128D818_NUM_REGS]; + uint16_t channel[ADC128D818_NUM_CHANNELS]; + + int16_t ain[ADC128D818_NUM_CHANNELS]; /* mV */ + int32_t temperature; /* milli-degrees Celsius */ + uint16_t ext_vref; /* mV, 0 means not connected */ + bool temp_alarm; /* temperature high-limit alarm latched */ + + char *description; +}; + +static uint16_t adc128d818_get_vref(const ADC128D818State *s) +{ + if (s->regs[REG_ADV_CONFIG] & ADV_CONFIG_EXT_REF_EN) { + if (s->ext_vref > 0u) { + return s->ext_vref; + } + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: external VREF selected but not" + " connected, falling back to internal\n", + __func__, s->description); + } + + return ADC128D818_INTERNAL_VREF_MV; +} + +static uint8_t adc128d818_get_mode(const ADC128D818State *s) +{ + return (s->regs[REG_ADV_CONFIG] & ADV_CONFIG_MODE_MASK) >> + ADV_CONFIG_MODE_SHIFT; +} + +static bool adc128d818_is_temp_channel(const ADC128D818State *s, unsigned ch) +{ + if (ch != 7u) { + return false; + } + + return adc128d818_get_mode(s) != 1u; +} + +static bool adc128d818_is_reserved_channel(const ADC128D818State *s, + unsigned ch) +{ + switch (adc128d818_get_mode(s)) { + case 2u: + return ch >= 4u && ch <= 6u; + case 3u: + return ch == 6u; + default: + return false; + } +} + +static int16_t adc128d818_channel_voltage(const ADC128D818State *s, unsigned ch) +{ + switch (adc128d818_get_mode(s)) { + case 2u: + switch (ch) { + case 0u: + return (int16_t)(s->ain[0] - s->ain[1]); + case 1u: + return (int16_t)(s->ain[3] - s->ain[2]); + case 2u: + return (int16_t)(s->ain[4] - s->ain[5]); + case 3u: + return (int16_t)(s->ain[7] - s->ain[6]); + default: + return 0; + } + case 3u: + switch (ch) { + case 4u: + return (int16_t)(s->ain[4] - s->ain[5]); + case 5u: + return (int16_t)(s->ain[7] - s->ain[6]); + default: + return s->ain[ch]; + } + default: + return s->ain[ch]; + } +} + +static void adc128d818_update_irq(ADC128D818State *s) +{ + uint8_t cfg = s->regs[REG_CONFIG]; + uint8_t active; + bool level; + + active = s->regs[REG_INT_STATUS] & ~s->regs[REG_INT_MASK]; + + /* INT pin is active-low */ + level = !((cfg & CONFIG_INT_ENABLE) && !(cfg & CONFIG_INT_CLEAR) && + (active != 0u)); + + trace_adc128d818_irq(s->description, level); + qemu_set_irq(s->irq, level); +} + +static bool adc128d818_monitoring_active(const ADC128D818State *s) +{ + if (s->regs[REG_DEEP_SHUTDOWN] & DEEP_SHUTDOWN_EN) { + return false; + } + if (!(s->regs[REG_CONFIG] & CONFIG_START)) { + return false; + } + if (s->regs[REG_CONFIG] & CONFIG_INT_CLEAR) { + return false; + } + + return true; +} + +static void adc128d818_check_limits(ADC128D818State *s) +{ + uint8_t disabled = s->regs[REG_CH_DISABLE]; + uint8_t int_status = 0u; + + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + if ((disabled & (1u << ch)) || + adc128d818_is_reserved_channel(s, ch)) { + continue; + } + + if (adc128d818_is_temp_channel(s, ch)) { + int raw = s->temperature / ADC128D818_TEMP_LSB_MC; + int thot; + int thyst; + + raw = MAX(ADC128D818_TEMP_RAW_MIN, + MIN(ADC128D818_TEMP_RAW_MAX, raw)); + thot = (int)(int8_t)s->regs[REG_LIMIT_BASE + ch * 2u] * 2; + thyst = (int)(int8_t)s->regs[REG_LIMIT_BASE + ch * 2u + 1u] * 2; + + if (raw > thot) { + s->temp_alarm = true; + } else if (raw <= thyst) { + s->temp_alarm = false; + } + if (s->temp_alarm) { + int_status |= (1u << ch); + } + } else { + uint8_t msb = (uint8_t)(s->channel[ch] >> 8u); + uint8_t high_lim = s->regs[REG_LIMIT_BASE + ch * 2u]; + uint8_t low_lim = s->regs[REG_LIMIT_BASE + ch * 2u + 1u]; + + if (msb > high_lim || msb <= low_lim) { + int_status |= (1u << ch); + } + } + } + + s->regs[REG_INT_STATUS] = int_status; + adc128d818_update_irq(s); +} + +static void adc128d818_convert(ADC128D818State *s) +{ + uint8_t disabled; + uint16_t vref; + + disabled = s->regs[REG_CH_DISABLE]; + vref = adc128d818_get_vref(s); + + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + if ((disabled & (1u << ch)) || + adc128d818_is_reserved_channel(s, ch)) { + continue; + } + + if (adc128d818_is_temp_channel(s, ch)) { + int32_t raw = s->temperature / ADC128D818_TEMP_LSB_MC; + + raw = + MAX(ADC128D818_TEMP_RAW_MIN, MIN(ADC128D818_TEMP_RAW_MAX, raw)); + s->channel[ch] = (uint16_t)(((unsigned)raw & 0x1FFu) << 7u); + } else { + int16_t vin = adc128d818_channel_voltage(s, ch); + int32_t dout; + + dout = vin * (int32_t)ADC128D818_ADC_RESOLUTION / vref; + dout = MAX(0, MIN((int32_t)ADC128D818_ADC_MAX, dout)); + s->channel[ch] = (uint16_t)(dout << 4u); + } + + trace_adc128d818_convert(s->description, ch, s->channel[ch]); + } + + s->regs[REG_BUSY_STATUS] &= ~BUSY_STATUS_NOT_READY; + + adc128d818_check_limits(s); +} + +static uint8_t adc128d818_read_channel(ADC128D818State *s, unsigned ch) +{ + uint8_t val; + + if (s->rx_byte == 0u) { + val = (uint8_t)(s->channel[ch] >> 8u); + trace_adc128d818_read_channel(s->description, ch, s->channel[ch]); + } else { + val = (uint8_t)(s->channel[ch] & 0xFFu); + } + s->rx_byte ^= 1u; + + return val; +} + +static uint8_t adc128d818_read_reg(ADC128D818State *s, uint8_t reg) +{ + uint8_t val; + + switch (reg) { + case REG_INT_STATUS: + val = s->regs[REG_INT_STATUS]; + s->regs[REG_INT_STATUS] = 0x00u; + if (adc128d818_monitoring_active(s)) { + adc128d818_check_limits(s); + } else { + adc128d818_update_irq(s); + } + trace_adc128d818_read(s->description, reg, val); + return val; + case REG_CONFIG: + case REG_INT_MASK: + case REG_CONV_RATE: + case REG_CH_DISABLE: + case REG_ONE_SHOT: + case REG_DEEP_SHUTDOWN: + case REG_ADV_CONFIG: + case REG_BUSY_STATUS: + case REG_LIMIT_BASE ... REG_LIMIT_LAST: + case REG_MANUFACTURER_ID: + case REG_REVISION_ID: + trace_adc128d818_read(s->description, reg, s->regs[reg]); + return s->regs[reg]; + case REG_CH_READING_BASE ... REG_CH_READING_LAST: + return adc128d818_read_channel(s, reg - REG_CH_READING_BASE); + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: read from undefined register 0x%02x\n", + __func__, s->description, reg); + return 0x00u; + } +} + +static void adc128d818_write_reg(ADC128D818State *s, uint8_t reg, uint8_t val); + +static void adc128d818_reset_regs(ADC128D818State *s) +{ + memset(s->regs, 0, sizeof(s->regs)); + memset(s->channel, 0, sizeof(s->channel)); + s->temp_alarm = false; + + s->regs[REG_CONFIG] = 0x08u; + s->regs[REG_BUSY_STATUS] = 0x02u; + s->regs[REG_MANUFACTURER_ID] = ADC128D818_MANUFACTURER_ID_VAL; + s->regs[REG_REVISION_ID] = ADC128D818_REVISION_ID_VAL; + + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + s->regs[REG_LIMIT_BASE + ch * 2u] = 0xFFu; + } + + s->pointer = 0x00u; + s->len = 0u; + s->rx_byte = 0u; + + adc128d818_update_irq(s); +} + +static void adc128d818_write_reg(ADC128D818State *s, uint8_t reg, uint8_t val) +{ + trace_adc128d818_write(s->description, reg, val); + + switch (reg) { + case REG_CONFIG: + if (val & CONFIG_INITIALIZATION) { + trace_adc128d818_reset(s->description, "reg"); + adc128d818_reset_regs(s); + break; + } + s->regs[REG_CONFIG] = val & CONFIG_WR_MASK; + if ((val & CONFIG_START) && !(val & CONFIG_INT_CLEAR) && + !(s->regs[REG_DEEP_SHUTDOWN] & DEEP_SHUTDOWN_EN)) { + adc128d818_convert(s); + } + adc128d818_update_irq(s); + break; + case REG_INT_MASK: + s->regs[REG_INT_MASK] = val; + adc128d818_update_irq(s); + break; + case REG_CONV_RATE: + if (s->regs[REG_CONFIG] & CONFIG_START) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: CONV_RATE written while running\n", + __func__, s->description); + break; + } + s->regs[REG_CONV_RATE] = val & CONV_RATE_MASK; + break; + case REG_CH_DISABLE: + if (s->regs[REG_CONFIG] & CONFIG_START) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: CH_DISABLE written while running\n", + __func__, s->description); + break; + } + s->regs[REG_CH_DISABLE] = val; + memset(s->channel, 0, sizeof(s->channel)); + s->regs[REG_INT_STATUS] = 0x00u; + s->temp_alarm = false; + adc128d818_update_irq(s); + break; + case REG_ONE_SHOT: + if (!(s->regs[REG_CONFIG] & CONFIG_START)) { + adc128d818_convert(s); + } + break; + case REG_DEEP_SHUTDOWN: + if ((val & DEEP_SHUTDOWN_EN) && (s->regs[REG_CONFIG] & CONFIG_START)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: DEEP_SHUTDOWN set while running\n", + __func__, s->description); + break; + } + s->regs[REG_DEEP_SHUTDOWN] = val & DEEP_SHUTDOWN_EN; + break; + case REG_ADV_CONFIG: + if (s->regs[REG_CONFIG] & CONFIG_START) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: ADV_CONFIG written while running\n", + __func__, s->description); + break; + } + s->regs[REG_ADV_CONFIG] = val & ADV_CONFIG_WR_MASK; + memset(s->channel, 0, sizeof(s->channel)); + s->regs[REG_INT_STATUS] = 0x00u; + s->temp_alarm = false; + adc128d818_update_irq(s); + break; + case REG_LIMIT_BASE ... REG_LIMIT_LAST: + s->regs[reg] = val; + break; + case REG_INT_STATUS: + case REG_BUSY_STATUS: + case REG_MANUFACTURER_ID: + case REG_REVISION_ID: + case REG_CH_READING_BASE ... REG_CH_READING_LAST: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: write to read-only register 0x%02x\n", + __func__, s->description, reg); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s: write to undefined register 0x%02x\n", + __func__, s->description, reg); + break; + } +} + +static uint8_t adc128d818_recv(I2CSlave *i2c) +{ + ADC128D818State *s = ADC128D818(i2c); + + return adc128d818_read_reg(s, s->pointer); +} + +static int adc128d818_send(I2CSlave *i2c, uint8_t data) +{ + ADC128D818State *s = ADC128D818(i2c); + + if (s->len == 0u) { + s->pointer = data; + s->len++; + } else { + adc128d818_write_reg(s, s->pointer, data); + } + + return 0; +} + +static int adc128d818_event(I2CSlave *i2c, enum i2c_event event) +{ + ADC128D818State *s = ADC128D818(i2c); + + s->len = 0u; + s->rx_byte = 0u; + + return 0; +} + +static void adc128d818_get_ain(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + int ch_num; + int rc; + + rc = sscanf(name, "ain%d", &ch_num); + if (rc != 1 || ch_num < 0 || ch_num >= (int)ADC128D818_NUM_CHANNELS) { + error_setg(errp, "%s: %s: invalid channel '%s'", __func__, + s->description, name); + return; + } + + value = s->ain[ch_num]; + visit_type_int(v, name, &value, errp); +} + +static void adc128d818_set_ain(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + int ch_num; + int rc; + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + rc = sscanf(name, "ain%d", &ch_num); + if (rc != 1 || ch_num < 0 || ch_num >= (int)ADC128D818_NUM_CHANNELS) { + error_setg(errp, "%s: %s: invalid channel '%s'", __func__, + s->description, name); + return; + } + + if (value < INT16_MIN || value > INT16_MAX) { + error_setg(errp, "%s: %s: value %" PRId64 " out of range for '%s'", + __func__, s->description, value, name); + return; + } + + s->ain[ch_num] = (int16_t)value; + + if (adc128d818_monitoring_active(s)) { + adc128d818_convert(s); + } +} + +static void adc128d818_get_temperature( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value = s->temperature; + + visit_type_int(v, name, &value, errp); +} + +static void adc128d818_set_temperature( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + if (value < INT32_MIN || value > INT32_MAX) { + error_setg(errp, "%s: %s: value %" PRId64 " out of range", __func__, + s->description, value); + return; + } + + s->temperature = (int32_t)value; + + if (adc128d818_monitoring_active(s)) { + adc128d818_convert(s); + } +} + +static const VMStateDescription adc128d818_vmstate = { + .name = "ADC128D818", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(len, ADC128D818State), + VMSTATE_UINT8(pointer, ADC128D818State), + VMSTATE_UINT8(rx_byte, ADC128D818State), + VMSTATE_UINT8_ARRAY(regs, ADC128D818State, + ADC128D818_NUM_REGS), + VMSTATE_UINT16_ARRAY(channel, ADC128D818State, + ADC128D818_NUM_CHANNELS), + VMSTATE_INT16_ARRAY(ain, ADC128D818State, + ADC128D818_NUM_CHANNELS), + VMSTATE_INT32(temperature, ADC128D818State), + VMSTATE_UINT16(ext_vref, ADC128D818State), + VMSTATE_BOOL(temp_alarm, ADC128D818State), + VMSTATE_I2C_SLAVE(parent_obj, ADC128D818State), + VMSTATE_END_OF_LIST() + } +}; + +static void adc128d818_reset_hold(Object *obj, ResetType type) +{ + ADC128D818State *s = ADC128D818(obj); + + trace_adc128d818_reset(s->description, "hw"); + adc128d818_reset_regs(s); +} + +static void adc128d818_get_ext_vref( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value = (int64_t)s->ext_vref; + + visit_type_int(v, name, &value, errp); +} + +static void adc128d818_set_ext_vref( + Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) +{ + ADC128D818State *s = ADC128D818(obj); + int64_t value; + + if (!visit_type_int(v, name, &value, errp)) { + return; + } + + if (value < 0 || value > ADC128D818_MAX_VDD_MV) { + error_setg(errp, + "%s: %s: ext-vref-mv %" PRId64 " out of range (0..%u mV)", + __func__, s->description, value, ADC128D818_MAX_VDD_MV); + return; + } + + s->ext_vref = (uint16_t)value; + + if (adc128d818_monitoring_active(s)) { + adc128d818_convert(s); + } +} + +static void adc128d818_initfn(Object *obj) +{ + for (unsigned ch = 0u; ch < ADC128D818_NUM_CHANNELS; ch++) { + char *name = g_strdup_printf("ain%u", ch); + + object_property_add(obj, name, "int", adc128d818_get_ain, + adc128d818_set_ain, NULL, NULL); + g_free(name); + } + + object_property_add(obj, "temperature", "int", adc128d818_get_temperature, + adc128d818_set_temperature, NULL, NULL); + object_property_add(obj, "ext-vref-mv", "int", adc128d818_get_ext_vref, + adc128d818_set_ext_vref, NULL, NULL); +} + +static void adc128d818_realize(DeviceState *dev, Error **errp) +{ + ADC128D818State *s = ADC128D818(dev); + + if (!s->description) { + s->description = g_strdup(object_get_typename(OBJECT(dev))); + } + + qdev_init_gpio_out(dev, &s->irq, 1u); +} + +static const Property adc128d818_properties[] = { + DEFINE_PROP_STRING("description", ADC128D818State, description), +}; + +static void adc128d818_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *ic = I2C_SLAVE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + ic->event = adc128d818_event; + ic->recv = adc128d818_recv; + ic->send = adc128d818_send; + dc->realize = adc128d818_realize; + rc->phases.hold = adc128d818_reset_hold; + dc->vmsd = &adc128d818_vmstate; + device_class_set_props(dc, adc128d818_properties); +} + +static const TypeInfo adc128d818_types[] = { + { + .name = TYPE_ADC128D818, + .parent = TYPE_I2C_SLAVE, + .instance_init = adc128d818_initfn, + .instance_size = sizeof(ADC128D818State), + .class_init = adc128d818_class_init, + }, +}; + +DEFINE_TYPES(adc128d818_types) diff --git a/hw/sensor/meson.build b/hw/sensor/meson.build index 420fdc3359..fe36c9ef91 100644 --- a/hw/sensor/meson.build +++ b/hw/sensor/meson.build @@ -1,3 +1,4 @@ +system_ss.add(when: 'CONFIG_ADC128D818', if_true: files('adc128d818.c')) system_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c')) system_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c')) system_ss.add(when: 'CONFIG_DPS310', if_true: files('dps310.c')) diff --git a/hw/sensor/trace-events b/hw/sensor/trace-events index a3fe54fa6d..5a3630f7bb 100644 --- a/hw/sensor/trace-events +++ b/hw/sensor/trace-events @@ -1,5 +1,13 @@ # See docs/devel/tracing.rst for syntax documentation. +# adc128d818.c +adc128d818_read(const char *id, uint8_t reg, uint8_t value) "%s reg 0x%02x val 0x%02x" +adc128d818_read_channel(const char *id, uint8_t channel, uint16_t value) "%s ch %u val 0x%04x" +adc128d818_write(const char *id, uint8_t reg, uint8_t value) "%s reg 0x%02x val 0x%02x" +adc128d818_convert(const char *id, uint8_t channel, uint16_t value) "%s ch %u val 0x%04x" +adc128d818_irq(const char *id, bool level) "%s level %u" +adc128d818_reset(const char *id, const char *source) "%s %s" + # tmp105.c tmp105_read(uint8_t dev, uint8_t addr) "device: 0x%02x, addr: 0x%02x" tmp105_write(uint8_t dev, uint8_t addr) "device: 0x%02x, addr 0x%02x" diff --git a/include/hw/sensor/adc128d818.h b/include/hw/sensor/adc128d818.h new file mode 100644 index 0000000000..10c34b9646 --- /dev/null +++ b/include/hw/sensor/adc128d818.h @@ -0,0 +1,14 @@ +/* + * Texas Instruments ADC128D818 12-bit 8-channel ADC with I2C interface + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_SENSOR_ADC128D818_H +#define HW_SENSOR_ADC128D818_H + +#define TYPE_ADC128D818 "adc128d818" + +#endif From 464e6551430d4d6e515f8a53a493b6fc860732c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Jul 2026 11:15:55 +0200 Subject: [PATCH 333/860] tests/qtest: adc128d818: add test harness and register access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the QOS test node and QMP property helpers for the ADC128D818, and cover basic register access: manufacturer and revision IDs, power-on-reset defaults, software reset, and the ain and temperature property readback. Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-3-emmanuel.blot@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/adc128d818-test.c | 169 ++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 2 files changed, 170 insertions(+) create mode 100644 tests/qtest/adc128d818-test.c diff --git a/tests/qtest/adc128d818-test.c b/tests/qtest/adc128d818-test.c new file mode 100644 index 0000000000..9a8811256f --- /dev/null +++ b/tests/qtest/adc128d818-test.c @@ -0,0 +1,169 @@ +/* + * QTest testcase for the ADC128D818 ADC + * + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "libqos/i2c.h" +#include "libqos/qgraph.h" +#include "libqtest-single.h" +#include "qobject/qdict.h" + +#define ADC128D818_TEST_ID "adc128d818-test" +#define ADC128D818_TEST_ADDR 0x1f + +/* Register addresses */ +#define REG_CONFIG 0x00 +#define REG_INT_STATUS 0x01 +#define REG_INT_MASK 0x03 +#define REG_CONV_RATE 0x07 +#define REG_CH_DISABLE 0x08 +#define REG_ONE_SHOT 0x09 +#define REG_DEEP_SHUTDOWN 0x0a +#define REG_ADV_CONFIG 0x0b +#define REG_BUSY_STATUS 0x0c + +/* Channel Reading Registers (16-bit, read-only) */ +#define REG_CH_READING_BASE 0x20 + +/* Limit Registers (8-bit, read/write) */ +#define REG_LIMIT_BASE 0x2a + +/* ID Registers (read-only) */ +#define REG_MANUFACTURER_ID 0x3e +#define REG_REVISION_ID 0x3f + +/* Configuration Register (0x00) bitfields */ +#define CONFIG_START BIT(0) +#define CONFIG_INT_ENABLE BIT(1) +#define CONFIG_INT_CLEAR BIT(3) +#define CONFIG_INITIALIZATION BIT(7) + +/* Advanced Configuration Register (0x0b) bitfields */ +#define ADV_CONFIG_EXT_REF_EN BIT(0) +#define ADV_CONFIG_MODE_1 (1 << 1) +#define ADV_CONFIG_MODE_2 (2 << 1) +#define ADV_CONFIG_MODE_3 (3 << 1) + +/* Number of channels */ +#define NUM_CHANNELS 8 + +/* Internal VREF in mV */ +#define INTERNAL_VREF_MV 2560 + +/* QMP helpers for setting device properties */ + +static void qmp_adc128d818_set(const char *property, int value) +{ + QDict *resp; + + resp = qmp("{ 'execute': 'qom-set', 'arguments':" + " { 'path': %s, 'property': %s, 'value': %d } }", + ADC128D818_TEST_ID, property, value); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +static int qmp_adc128d818_get(const char *property) +{ + QDict *resp; + int ret; + + resp = qmp("{ 'execute': 'qom-get', 'arguments':" + " { 'path': %s, 'property': %s } }", + ADC128D818_TEST_ID, property); + g_assert(qdict_haskey(resp, "return")); + ret = qdict_get_int(resp, "return"); + qobject_unref(resp); + return ret; +} + +/* Manufacturer and Revision ID registers */ +static void test_id_registers(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, REG_MANUFACTURER_ID), ==, 0x01); + g_assert_cmphex(i2c_get8(dev, REG_REVISION_ID), ==, 0x09); +} + +/* Power-on-reset default values */ +static void test_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + unsigned ch; + + g_assert_cmphex(i2c_get8(dev, REG_CONFIG), ==, 0x08); + g_assert_cmphex(i2c_get8(dev, REG_INT_STATUS), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_INT_MASK), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_CONV_RATE), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_CH_DISABLE), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_DEEP_SHUTDOWN), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_ADV_CONFIG), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS), ==, 0x02); + + for (ch = 0u; ch < NUM_CHANNELS; ch++) { + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE + ch * 2u), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE + ch * 2u + 1u), ==, 0x00); + } +} + +/* Software reset via INITIALIZATION bit */ +static void test_soft_reset(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, REG_INT_MASK, 0xAA); + i2c_set8(dev, REG_CH_DISABLE, 0x55); + i2c_set8(dev, REG_LIMIT_BASE, 0x42); + + g_assert_cmphex(i2c_get8(dev, REG_INT_MASK), ==, 0xAA); + g_assert_cmphex(i2c_get8(dev, REG_CH_DISABLE), ==, 0x55); + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE), ==, 0x42); + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + g_assert_cmphex(i2c_get8(dev, REG_CONFIG), ==, 0x08); + g_assert_cmphex(i2c_get8(dev, REG_INT_MASK), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_CH_DISABLE), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS), ==, 0x02); +} + +/* Verify ain property readback via QMP */ +static void test_ain_property(void *obj, void *data, QGuestAllocator *alloc) +{ + int value; + + qmp_adc128d818_set("ain3", 1500); + value = qmp_adc128d818_get("ain3"); + g_test_message("Set ain3 = 1500 mV, readback = %d mV", value); + g_assert_cmpint(value, ==, 1500); + + qmp_adc128d818_set("temperature", 37500); + value = qmp_adc128d818_get("temperature"); + g_test_message("Set temperature = 37500 mC, readback = %d mC", value); + g_assert_cmpint(value, ==, 37500); +} + +static void adc128d818_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "id=" ADC128D818_TEST_ID + ",address=0x1f" + }; + add_qi2c_address(&opts, &(QI2CAddress) { ADC128D818_TEST_ADDR }); + + qos_node_create_driver("adc128d818", i2c_device_create); + qos_node_consumes("adc128d818", "i2c-bus", &opts); + + qos_add_test("id-registers", "adc128d818", test_id_registers, NULL); + qos_add_test("defaults", "adc128d818", test_defaults, NULL); + qos_add_test("soft-reset", "adc128d818", test_soft_reset, NULL); + qos_add_test("ain-property", "adc128d818", test_ain_property, NULL); +} +libqos_init(adc128d818_register_nodes); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 56ff860e21..5e1fc597e6 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -304,6 +304,7 @@ qtests_hexagon = ['boot-serial-test'] qos_test_ss = ss.source_set() qos_test_ss.add( 'ac97-test.c', + 'adc128d818-test.c', 'adm1272-test.c', 'adm1266-test.c', 'ds1338-test.c', From c7300dca454f266a5cc52ea08775db50489cc755 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Jul 2026 11:15:56 +0200 Subject: [PATCH 334/860] tests/qtest: adc128d818: test voltage and temperature conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover single-ended voltage conversion across all channels, voltage and temperature boundary and clamping cases, and scaling against an external voltage reference. Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-4-emmanuel.blot@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/adc128d818-test.c | 189 ++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) diff --git a/tests/qtest/adc128d818-test.c b/tests/qtest/adc128d818-test.c index 9a8811256f..891189f0bd 100644 --- a/tests/qtest/adc128d818-test.c +++ b/tests/qtest/adc128d818-test.c @@ -150,6 +150,186 @@ static void test_ain_property(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpint(value, ==, 37500); } +/* Voltage conversion */ +static void test_voltage_conversion(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + qmp_adc128d818_set("ain0", 1280); + g_test_message("Injected ain0 = 1280 mV"); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain1", 2560); + g_test_message("Injected ain1 = 2560 mV"); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Read ch1: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0xFFF0); + + qmp_adc128d818_set("ain2", 0); + g_test_message("Injected ain2 = 0 mV"); + reading = i2c_get16(dev, REG_CH_READING_BASE + 2u); + g_test_message("Read ch2: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Temperature conversion (mode 0, channel 7 = temperature) */ +static void +test_temperature_conversion(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + qmp_adc128d818_set("temperature", 25000); + g_test_message("Injected temperature = 25000 mC (25.0 deg C)"); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("Read ch7: raw 0x%04x -> %d mC", reading, + (int16_t)(reading & 0xFF80u) * 500 / 128); + g_assert_cmphex(reading, ==, 0x1900); +} + +/* Channels with distinct voltages */ +static void test_all_channels(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + static const uint16_t ain_mv[NUM_CHANNELS] = { + 0, 320, 640, 960, 1280, 1920, 2240, 2560 + }; + static const uint16_t expect[NUM_CHANNELS] = { + 0x0000, 0x2000, 0x4000, 0x6000, 0x8000, 0xC000, 0xE000, 0xFFF0 + }; + uint16_t reading; + unsigned ch; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + + for (ch = 0u; ch < NUM_CHANNELS; ch++) { + char name[8]; + snprintf(name, sizeof(name), "ain%u", ch); + qmp_adc128d818_set(name, ain_mv[ch]); + } + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + for (ch = 0u; ch < NUM_CHANNELS; ch++) { + reading = i2c_get16(dev, REG_CH_READING_BASE + ch); + g_test_message("ch%u: ain %u mV -> raw 0x%04x (expect 0x%04x)", + ch, ain_mv[ch], reading, expect[ch]); + g_assert_cmphex(reading, ==, expect[ch]); + } +} + +/* Voltage conversion edge cases */ +static void test_voltage_edges(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + + qmp_adc128d818_set("ain0", 3000); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Over-range 3000 mV: raw 0x%04x (expect 0xFFF0)", reading); + g_assert_cmphex(reading, ==, 0xFFF0); + + qmp_adc128d818_set("ain1", 1); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("1 mV: raw 0x%04x (expect 0x0010)", reading); + g_assert_cmphex(reading, ==, 0x0010); + + qmp_adc128d818_set("ain2", 640); + reading = i2c_get16(dev, REG_CH_READING_BASE + 2u); + g_test_message("640 mV (quarter): raw 0x%04x (expect 0x4000)", reading); + g_assert_cmphex(reading, ==, 0x4000); + + qmp_adc128d818_set("ain3", 1920); + reading = i2c_get16(dev, REG_CH_READING_BASE + 3u); + g_test_message("1920 mV (3/4): raw 0x%04x (expect 0xC000)", reading); + g_assert_cmphex(reading, ==, 0xC000); +} + +/* Temperature conversion edge cases */ +static void test_temperature_edges(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("temperature", 0); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("0 C: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("temperature", -25000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("-25 C: raw 0x%04x (expect 0xE700)", reading); + g_assert_cmphex(reading, ==, 0xE700); + + qmp_adc128d818_set("temperature", 127500); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("+127.5 C: raw 0x%04x (expect 0x7F80)", reading); + g_assert_cmphex(reading, ==, 0x7F80); + + qmp_adc128d818_set("temperature", -128000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("-128 C: raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("temperature", 200000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("200 C (clamped): raw 0x%04x (expect 0x7F80)", reading); + g_assert_cmphex(reading, ==, 0x7F80); + + qmp_adc128d818_set("temperature", -200000); + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("-200 C (clamped): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* External voltage reference */ +static void test_ext_vref(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + + qmp_adc128d818_set("ext-vref-mv", 4096); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_EXT_REF_EN | ADV_CONFIG_MODE_1); + + qmp_adc128d818_set("ain0", 1000); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("1000 mV / 4096 mV VREF: raw 0x%04x (expect 0x3E80)", + reading); + g_assert_cmphex(reading, ==, 0x3E80); + + qmp_adc128d818_set("ain1", 2048); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("2048 mV / 4096 mV VREF: raw 0x%04x (expect 0x8000)", + reading); + g_assert_cmphex(reading, ==, 0x8000); +} + static void adc128d818_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -165,5 +345,14 @@ static void adc128d818_register_nodes(void) qos_add_test("defaults", "adc128d818", test_defaults, NULL); qos_add_test("soft-reset", "adc128d818", test_soft_reset, NULL); qos_add_test("ain-property", "adc128d818", test_ain_property, NULL); + qos_add_test("voltage-conversion", "adc128d818", test_voltage_conversion, + NULL); + qos_add_test("temperature-conversion", "adc128d818", + test_temperature_conversion, NULL); + qos_add_test("all-channels", "adc128d818", test_all_channels, NULL); + qos_add_test("voltage-edges", "adc128d818", test_voltage_edges, NULL); + qos_add_test("temperature-edges", "adc128d818", test_temperature_edges, + NULL); + qos_add_test("ext-vref", "adc128d818", test_ext_vref, NULL); } libqos_init(adc128d818_register_nodes); From af685633f45a5ad9ed052cc5e58c7fe62bd0c631 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Jul 2026 11:15:57 +0200 Subject: [PATCH 335/860] tests/qtest: adc128d818: test limit interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover per-channel high- and low-limit interrupt status, the INT_CLEAR bit gating the monitoring loop, and the temperature high-limit alarm with hysteresis. Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-5-emmanuel.blot@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/adc128d818-test.c | 121 ++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/tests/qtest/adc128d818-test.c b/tests/qtest/adc128d818-test.c index 891189f0bd..3e3fabb8d9 100644 --- a/tests/qtest/adc128d818-test.c +++ b/tests/qtest/adc128d818-test.c @@ -330,6 +330,123 @@ static void test_ext_vref(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(reading, ==, 0x8000); } +/* Interrupt status set on limit violation; persists while fault remains */ +static void test_interrupt_status(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_LIMIT_BASE, 0x10); + g_test_message("Set ch0 high limit = 0x10"); + + qmp_adc128d818_set("ain0", 2560); + g_test_message("Injected ain0 = 2560 mV (exceeds limit)"); + + i2c_set8(dev, REG_CONFIG, CONFIG_START | CONFIG_INT_ENABLE); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS = 0x%02x (expect bit 0 set)", status); + g_assert_cmphex(status & 0x01u, ==, 0x01); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS after re-read = 0x%02x (expect bit 0 still set)", + status); + g_assert_cmphex(status & 0x01u, ==, 0x01); + + qmp_adc128d818_set("ain0", 80); + g_test_message("Injected ain0 = 80 mV (within limit)"); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS after fault cleared = 0x%02x " + "(expect bit 0 clear)", status); + g_assert_cmphex(status & 0x01u, ==, 0x00); +} + +/* INT_CLEAR stops the round-robin monitoring loop */ +static void test_int_clear(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_LIMIT_BASE, 0x10); + qmp_adc128d818_set("ain0", 2560); + + i2c_set8(dev, REG_CONFIG, + CONFIG_START | CONFIG_INT_ENABLE | CONFIG_INT_CLEAR); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS with INT_CLEAR set = 0x%02x (expect 0x00)", + status); + g_assert_cmphex(status, ==, 0x00); + + i2c_set8(dev, REG_CONFIG, CONFIG_START | CONFIG_INT_ENABLE); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS after INT_CLEAR cleared = 0x%02x (expect bit 0)", + status); + g_assert_cmphex(status & 0x01u, ==, 0x01); +} + +/* Low-limit interrupt triggers correctly */ +static void test_low_limit(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + i2c_set8(dev, REG_LIMIT_BASE + 3u, 0x80); + g_test_message("Set ch1 low limit = 0x80"); + + i2c_set8(dev, REG_LIMIT_BASE + 5u, 0x80); + g_test_message("Set ch2 low limit = 0x80"); + + qmp_adc128d818_set("ain1", 640); + qmp_adc128d818_set("ain2", 1280); + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START | CONFIG_INT_ENABLE); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("INT_STATUS = 0x%02x (expect bits 1 and 2 set)", status); + g_assert_cmphex(status & 0x02u, ==, 0x02); + g_assert_cmphex(status & 0x04u, ==, 0x04); + + g_assert_cmphex(status & 0x01u, ==, 0x00); +} + +/* Temperature high-limit interrupt with hysteresis */ +static void test_temp_hysteresis(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t status; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + i2c_set8(dev, REG_LIMIT_BASE + 7u * 2u, 0x32); + i2c_set8(dev, REG_LIMIT_BASE + 7u * 2u + 1u, 0x28); + + qmp_adc128d818_set("temperature", 25000); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("25 C: INT_STATUS = 0x%02x (temp bit expect clear)", status); + g_assert_cmphex(status & 0x80u, ==, 0x00); + + qmp_adc128d818_set("temperature", 55000); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("55 C: INT_STATUS = 0x%02x (temp bit expect set)", status); + g_assert_cmphex(status & 0x80u, ==, 0x80); + + qmp_adc128d818_set("temperature", 45000); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("45 C (hysteresis): INT_STATUS = 0x%02x " + "(temp bit expect set)", status); + g_assert_cmphex(status & 0x80u, ==, 0x80); + + qmp_adc128d818_set("temperature", 35000); + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("35 C: INT_STATUS = 0x%02x (temp bit expect clear)", status); + g_assert_cmphex(status & 0x80u, ==, 0x00); +} + static void adc128d818_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -354,5 +471,9 @@ static void adc128d818_register_nodes(void) qos_add_test("temperature-edges", "adc128d818", test_temperature_edges, NULL); qos_add_test("ext-vref", "adc128d818", test_ext_vref, NULL); + qos_add_test("interrupt-status", "adc128d818", test_interrupt_status, NULL); + qos_add_test("int-clear", "adc128d818", test_int_clear, NULL); + qos_add_test("low-limit", "adc128d818", test_low_limit, NULL); + qos_add_test("temp-hysteresis", "adc128d818", test_temp_hysteresis, NULL); } libqos_init(adc128d818_register_nodes); From 41bea707fef524b5457742d1786bece491d681d8 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Jul 2026 11:15:58 +0200 Subject: [PATCH 336/860] tests/qtest: adc128d818: test operating modes and power control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover advanced-configuration mode selection (single-ended, pseudo-differential pairs, and mixed) and the reset of readings on reconfiguration, plus channel disable, one-shot conversion, deep shutdown, BUSY_STATUS lifecycle, and conversion-rate gating. Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-6-emmanuel.blot@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/adc128d818-test.c | 377 ++++++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) diff --git a/tests/qtest/adc128d818-test.c b/tests/qtest/adc128d818-test.c index 3e3fabb8d9..91eda5ca74 100644 --- a/tests/qtest/adc128d818-test.c +++ b/tests/qtest/adc128d818-test.c @@ -447,6 +447,368 @@ static void test_temp_hysteresis(void *obj, void *data, g_assert_cmphex(status & 0x80u, ==, 0x00); } +/* Channel disable prevents conversion */ +static void test_channel_disable(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CH_DISABLE, 0x01); + g_test_message("Disabled channel 0"); + + qmp_adc128d818_set("ain0", 1280); + g_test_message("Injected ain0 = 1280 mV (disabled)"); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0 (disabled): raw 0x%04x", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("ain1", 1280); + g_test_message("Injected ain1 = 1280 mV (enabled)"); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Read ch1 (enabled): raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* One-shot conversion in shutdown mode */ +static void test_one_shot(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + qmp_adc128d818_set("ain0", 1280); + g_test_message("Injected ain0 = 1280 mV (device stopped)"); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0 before one-shot: raw 0x%04x", reading); + g_assert_cmphex(reading, ==, 0x0000); + + g_assert_cmphex(i2c_get8(dev, REG_ONE_SHOT), ==, 0x00); + + i2c_set8(dev, REG_ONE_SHOT, 0x00); + g_test_message("Triggered one-shot conversion with value 0x00"); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Read ch0 after one-shot: raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* Mode 1 makes channel 7 a voltage input instead of temperature */ +static void test_mode_selection(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_1); + g_test_message("Set mode 1 (all voltage channels)"); + + qmp_adc128d818_set("ain7", 1280); + qmp_adc128d818_set("temperature", 50000); + g_test_message("Injected ain7 = 1280 mV, temperature = 50000 mC"); + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("Read ch7 (mode 1): raw 0x%04x -> %u mV", reading, + (reading >> 4u) * INTERNAL_VREF_MV / 4096u); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* Mode 2 - 4 pseudo-differential pairs */ +static void test_mode2_diff(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_2); + g_test_message("Set mode 2 (4 pseudo-differential pairs)"); + + qmp_adc128d818_set("ain0", 2000); + qmp_adc128d818_set("ain1", 720); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Pair 0 (IN0-IN1): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain3", 1920); + qmp_adc128d818_set("ain2", 640); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Pair 1 (IN3-IN2): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain4", 1500); + qmp_adc128d818_set("ain5", 220); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 2u); + g_test_message("Pair 2 (IN4-IN5): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain7", 2560); + qmp_adc128d818_set("ain6", 1280); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 3u); + g_test_message("Pair 3 (IN7-IN6): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 4u); + g_test_message("Reserved ch4: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("ain0", 500); + qmp_adc128d818_set("ain1", 1000); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Pair 0 negative dV: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Mode 3 - 4 single-ended + 2 pseudo-differential pairs */ +static void test_mode3_mixed(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_3); + g_test_message("Set mode 3 (4 single-ended + 2 differential)"); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Ch0 single-ended: raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain4", 1500); + qmp_adc128d818_set("ain5", 220); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 4u); + g_test_message("Ch4 diff (IN4-IN5): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain7", 2560); + qmp_adc128d818_set("ain6", 1280); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 5u); + g_test_message("Ch5 diff (IN7-IN6): raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 6u); + g_test_message("Reserved ch6: raw 0x%04x (expect 0x0000)", reading); + g_assert_cmphex(reading, ==, 0x0000); + + qmp_adc128d818_set("temperature", 25000); + + reading = i2c_get16(dev, REG_CH_READING_BASE + 7u); + g_test_message("Ch7 temperature: raw 0x%04x (expect 0x1900)", reading); + g_assert_cmphex(reading, ==, 0x1900); +} + +/* Mode change resets channel readings and interrupt status */ +static void test_mode_change_reset(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + uint8_t status; + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Before mode change, ch0: raw 0x%04x", reading); + g_assert_cmphex(reading, !=, 0x0000); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_LIMIT_BASE, 0x10); + qmp_adc128d818_set("ain0", 2560); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_2); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After mode change, ch0: raw 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); + + status = i2c_get8(dev, REG_INT_STATUS); + g_test_message("After mode change, INT_STATUS: 0x%02x (expect 0x00)", + status); + g_assert_cmphex(status, ==, 0x00); + + g_assert_cmphex(i2c_get8(dev, REG_LIMIT_BASE), ==, 0x10); + g_test_message("Limit register preserved after mode change"); +} + +/* QOM property changes trigger correct differential conversion */ +static void test_diff_qom_trigger(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_MODE_2); + + qmp_adc128d818_set("ain0", 0); + qmp_adc128d818_set("ain1", 0); + qmp_adc128d818_set("ain2", 0); + qmp_adc128d818_set("ain3", 0); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + qmp_adc128d818_set("ain0", 2000); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After ain0=2000, ain1=0: pair0 = 0x%04x (expect 0xC800)", + reading); + g_assert_cmphex(reading, ==, 0xC800); + + qmp_adc128d818_set("ain1", 720); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After ain1=720: pair0 = 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); + + qmp_adc128d818_set("ain3", 1920); + qmp_adc128d818_set("ain2", 640); + reading = i2c_get16(dev, REG_CH_READING_BASE + 1u); + g_test_message("Pair 1 (IN3-IN2) via QOM: 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* One-shot conversion works in deep shutdown */ +static void test_deep_shutdown(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_assert_cmphex(reading, ==, 0x8000); + + i2c_set8(dev, REG_DEEP_SHUTDOWN, 0x01); + g_test_message("DEEP_SHUTDOWN write while running rejected"); + g_assert_cmphex(i2c_get8(dev, REG_DEEP_SHUTDOWN), ==, 0x00); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_DEEP_SHUTDOWN, 0x01); + qmp_adc128d818_set("ain0", 0); + + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Deep shutdown, no one-shot: raw 0x%04x (expect 0x8000)", + reading); + g_assert_cmphex(reading, ==, 0x8000); + + i2c_set8(dev, REG_ONE_SHOT, 0x01); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("Deep shutdown one-shot: raw 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); + + g_assert_cmphex(i2c_get8(dev, REG_DEEP_SHUTDOWN), ==, 0x01); + + i2c_set8(dev, REG_DEEP_SHUTDOWN, 0x00); + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_ONE_SHOT, 0x01); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After exit shutdown: raw 0x%04x (expect 0x8000)", reading); + g_assert_cmphex(reading, ==, 0x8000); +} + +/* BUSY_STATUS NOT_READY clears after first conversion */ +static void test_busy_status(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS) & 0x02, ==, 0x02); + g_test_message("After reset: BUSY_STATUS = 0x%02x (NOT_READY set)", + i2c_get8(dev, REG_BUSY_STATUS)); + + qmp_adc128d818_set("ain0", 0); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + + g_assert_cmphex(i2c_get8(dev, REG_BUSY_STATUS) & 0x02, ==, 0x00); + g_test_message("After conversion: BUSY_STATUS = 0x%02x (NOT_READY cleared)", + i2c_get8(dev, REG_BUSY_STATUS)); +} + +/* Programming Channel Disable resets channel readings */ +static void test_chan_disable_clears(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + g_assert_cmphex(i2c_get16(dev, REG_CH_READING_BASE), ==, 0x8000); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_CH_DISABLE, 0x02); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After CH_DISABLE write: ch0 raw 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Programming Advanced Configuration always resets channel readings */ +static void test_adv_config_clears(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint16_t reading; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + qmp_adc128d818_set("ain0", 1280); + i2c_set8(dev, REG_CONFIG, CONFIG_START); + g_assert_cmphex(i2c_get16(dev, REG_CH_READING_BASE), ==, 0x8000); + + i2c_set8(dev, REG_CONFIG, 0x00); + i2c_set8(dev, REG_ADV_CONFIG, 0x00); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After same-mode ADV_CONFIG: ch0 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + g_assert_cmphex(i2c_get16(dev, REG_CH_READING_BASE), ==, 0x8000); + i2c_set8(dev, REG_CONFIG, 0x00); + qmp_adc128d818_set("ext-vref-mv", 4096); + i2c_set8(dev, REG_ADV_CONFIG, ADV_CONFIG_EXT_REF_EN); + reading = i2c_get16(dev, REG_CH_READING_BASE); + g_test_message("After ext-vref toggle: ch0 0x%04x (expect 0x0000)", + reading); + g_assert_cmphex(reading, ==, 0x0000); +} + +/* Conversion Rate register may only be programmed while in shutdown */ +static void test_conv_rate(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, REG_CONFIG, CONFIG_INITIALIZATION); + + i2c_set8(dev, REG_CONV_RATE, 0x01); + g_assert_cmphex(i2c_get8(dev, REG_CONV_RATE), ==, 0x01); + + i2c_set8(dev, REG_CONFIG, CONFIG_START); + i2c_set8(dev, REG_CONV_RATE, 0x00); + g_test_message("CONV_RATE while running: 0x%02x (expect unchanged 0x01)", + i2c_get8(dev, REG_CONV_RATE)); + g_assert_cmphex(i2c_get8(dev, REG_CONV_RATE), ==, 0x01); +} + static void adc128d818_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -475,5 +837,20 @@ static void adc128d818_register_nodes(void) qos_add_test("int-clear", "adc128d818", test_int_clear, NULL); qos_add_test("low-limit", "adc128d818", test_low_limit, NULL); qos_add_test("temp-hysteresis", "adc128d818", test_temp_hysteresis, NULL); + qos_add_test("channel-disable", "adc128d818", test_channel_disable, NULL); + qos_add_test("one-shot", "adc128d818", test_one_shot, NULL); + qos_add_test("mode-selection", "adc128d818", test_mode_selection, NULL); + qos_add_test("mode2-diff", "adc128d818", test_mode2_diff, NULL); + qos_add_test("mode3-mixed", "adc128d818", test_mode3_mixed, NULL); + qos_add_test("mode-change-reset", "adc128d818", test_mode_change_reset, + NULL); + qos_add_test("diff-qom-trigger", "adc128d818", test_diff_qom_trigger, NULL); + qos_add_test("deep-shutdown", "adc128d818", test_deep_shutdown, NULL); + qos_add_test("busy-status", "adc128d818", test_busy_status, NULL); + qos_add_test("chan-disable-clears", "adc128d818", test_chan_disable_clears, + NULL); + qos_add_test("adv-config-clears", "adc128d818", test_adv_config_clears, + NULL); + qos_add_test("conv-rate", "adc128d818", test_conv_rate, NULL); } libqos_init(adc128d818_register_nodes); From b3e56ab9197283fdb911c0fe7cdc20f45ed2d789 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Jul 2026 11:16:00 +0200 Subject: [PATCH 337/860] hw/arm/aspeed: anacapa: use ASCII in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The anacapa machine source contains a few comments using the U+2014 EM DASH character. Replace them with plain ASCII hyphens so the file stays ASCII-only. Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-8-emmanuel.blot@free.fr Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600_anacapa.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed_ast2600_anacapa.c b/hw/arm/aspeed_ast2600_anacapa.c index a1c8111a93..bd6cc00686 100644 --- a/hw/arm/aspeed_ast2600_anacapa.c +++ b/hw/arm/aspeed_ast2600_anacapa.c @@ -242,7 +242,7 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c1 */ /* eeprom@50 */ at24c_eeprom_init(i2c[1], 0x50, 256 * KiB); - /* i2c-mux@70 (PCA9546) — 4 channels, empty */ + /* i2c-mux@70 (PCA9546) - 4 channels, empty */ i2c_slave_create_simple(i2c[1], TYPE_PCA9546, 0x70); /* &i2c4 */ @@ -259,7 +259,7 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) i2c_mux = i2c_slave_create_simple(i2c[8], TYPE_PCA9546, 0x72); /* i2c8mux ch0 */ - /* adc128d818@1f — no model */ + /* adc128d818@1f - no model */ /* pca9555@22 */ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), TYPE_PCA9552, 0x22); @@ -305,7 +305,7 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) /* i2c-mux@71 (PCA9548) */ i2c_mux = i2c_slave_create_simple(i2c[11], TYPE_PCA9548, 0x71); - /* i2c11mux ch0-ch4 — empty */ + /* i2c11mux ch0-ch4 - empty */ /* i2c11mux ch5 */ /* pca9555@22 */ @@ -328,7 +328,7 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) hpm_brd_id_eeprom, hpm_brd_id_eeprom_len); /* i2c13mux ch7 */ - /* nfc@28 — no model */ + /* nfc@28 - no model */ } static void aspeed_machine_anacapa_class_init(ObjectClass *oc, From 15d88540b83074807a483b35a7a6744571fcd9cb Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Tue, 7 Jul 2026 11:16:01 +0200 Subject: [PATCH 338/860] hw/arm: anacapa: add ADC128D818 devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire up the two ADC128D818 instances that appear in the Anacapa DTS: one on i2c8 mux channel 0 and one on i2c13 mux channel 3. Reviewed-by: Cédric Le Goater Signed-off-by: Emmanuel Blot Link: https://lore.kernel.org/qemu-devel/20260707091609.97759-9-emmanuel.blot@free.fr Signed-off-by: Cédric Le Goater --- hw/arm/Kconfig | 1 + hw/arm/aspeed_ast2600_anacapa.c | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 5869f0f9e8..c502e82847 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -558,6 +558,7 @@ config ASPEED_SOC select LED select PMBUS select MAX31785 + select ADC128D818 select FSI_APB2OPB_ASPEED select AT24C select PCI_EXPRESS_ASPEED diff --git a/hw/arm/aspeed_ast2600_anacapa.c b/hw/arm/aspeed_ast2600_anacapa.c index bd6cc00686..65d6b0faa2 100644 --- a/hw/arm/aspeed_ast2600_anacapa.c +++ b/hw/arm/aspeed_ast2600_anacapa.c @@ -1,13 +1,14 @@ /* * Facebook Anacapa * - * Copyright (c) Meta Platforms, Inc. and affiliates. + * Copyright (c) 2026 Meta Platforms, Inc. and affiliates. * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" #include "qapi/error.h" +#include "hw/sensor/adc128d818.h" #include "hw/arm/machines-qom.h" #include "hw/arm/aspeed.h" #include "hw/arm/aspeed_soc.h" @@ -15,7 +16,6 @@ #include "hw/gpio/pca9552.h" #include "hw/nvram/eeprom_at24c.h" -/* Anacapa hardware value */ #define ANACAPA_BMC_HW_STRAP1 0x00002002 #define ANACAPA_BMC_HW_STRAP2 0x00000000 #define ANACAPA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) @@ -221,6 +221,17 @@ static const uint8_t hpm_brd_id_eeprom[] = { }; static const size_t hpm_brd_id_eeprom_len = sizeof(hpm_brd_id_eeprom); +static void anacapa_add_adc128d818(I2CBus *bus, uint8_t addr, + const char *description) +{ + DeviceState *dev = DEVICE(i2c_slave_new(TYPE_ADC128D818, addr)); + g_autofree char *childname = g_strdup_printf("0x%02x", addr); + + qdev_prop_set_string(dev, "description", description); + object_property_add_child(OBJECT(bus), childname, OBJECT(dev)); + i2c_slave_realize_and_unref(I2C_SLAVE(dev), bus, &error_fatal); +} + static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) { /* Reference: aspeed-bmc-facebook-anacapa.dts */ @@ -259,7 +270,8 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) i2c_mux = i2c_slave_create_simple(i2c[8], TYPE_PCA9546, 0x72); /* i2c8mux ch0 */ - /* adc128d818@1f - no model */ + /* adc128d818@1f - R-PDB ADC (mode 1: 8 voltage channels) */ + anacapa_add_adc128d818(pca954x_i2c_get_bus(i2c_mux, 0), 0x1f, "i2c8:0:1f"); /* pca9555@22 */ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), TYPE_PCA9552, 0x22); @@ -320,7 +332,8 @@ static void anacapa_bmc_i2c_init(AspeedMachineState *bmc) i2c_mux = i2c_slave_create_simple(i2c[13], TYPE_PCA9548, 0x70); /* i2c13mux ch3 */ - /* adc128d818@1f - no model */ + /* adc128d818@1f - MB ADC (mode 1: 8 voltage channels) */ + anacapa_add_adc128d818(pca954x_i2c_get_bus(i2c_mux, 3), 0x1f, "i2c13:3:1f"); /* i2c13mux ch4 */ /* eeprom@51 */ From 39cb912af93ebb64d4639ef4780ff0f08a0c1a60 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:39 +0200 Subject: [PATCH 339/860] hw/gpio: pca9552: register types with DEFINE_TYPES() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the separate TypeInfo definitions and pca955x_register_types() registration function with a single type array registered through the DEFINE_TYPES() macro, to prepare addition of new PCA955x-derived devices. No functional change. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-1-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 49 ++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index b13ac9fd9c..50e868a6e9 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -492,16 +492,6 @@ static void pca955x_class_init(ObjectClass *klass, const void *data) device_class_set_props(dc, pca955x_properties); } -static const TypeInfo pca955x_info = { - .name = TYPE_PCA955X, - .parent = TYPE_I2C_SLAVE, - .instance_init = pca955x_initfn, - .instance_size = sizeof(PCA955xState), - .class_init = pca955x_class_init, - .class_size = sizeof(PCA955xClass), - .abstract = true, -}; - static void pca9552_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -526,23 +516,26 @@ static void pca9535_class_init(ObjectClass *oc, const void *data) pc->has_led_support = false; } -static const TypeInfo pca9552_info = { - .name = TYPE_PCA9552, - .parent = TYPE_PCA955X, - .class_init = pca9552_class_init, +static const TypeInfo pca955x_types[] = { + { + .name = TYPE_PCA955X, + .parent = TYPE_I2C_SLAVE, + .instance_init = pca955x_initfn, + .instance_size = sizeof(PCA955xState), + .class_init = pca955x_class_init, + .class_size = sizeof(PCA955xClass), + .abstract = true, + }, + { + .name = TYPE_PCA9552, + .parent = TYPE_PCA955X, + .class_init = pca9552_class_init, + }, + { + .name = TYPE_PCA9535, + .parent = TYPE_PCA955X, + .class_init = pca9535_class_init, + } }; -static const TypeInfo pca9535_info = { - .name = TYPE_PCA9535, - .parent = TYPE_PCA955X, - .class_init = pca9535_class_init, -}; - -static void pca955x_register_types(void) -{ - type_register_static(&pca955x_info); - type_register_static(&pca9552_info); - type_register_static(&pca9535_info); -} - -type_init(pca955x_register_types) +DEFINE_TYPES(pca955x_types) From 9c995025830f1b1ee7941913dd178bf3e22a976a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:40 +0200 Subject: [PATCH 340/860] hw/gpio: pca9552: move PCA955xState definition out of the header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing outside pca9552.c uses the PCA955xState structure, its instance checker, or the PCA955X_NR_REGS/PCA955X_PIN_COUNT_MAX defines: the board files and qtests only rely on the TYPE_* name macros (and the register macros in pca9552_regs.h). Move the state structure and the size defines into pca9552.c, leaving pca9552.h with just the type-name macros. While at it, replace the separate DECLARE_INSTANCE_CHECKER and DECLARE_CLASS_CHECKERS declarations with a single OBJECT_DECLARE_TYPE(). Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-2-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 29 ++++++++++++++++++++++++----- include/hw/gpio/pca9552.h | 30 ++++-------------------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 50e868a6e9..36bc4f86ed 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -1,7 +1,10 @@ /* - * PCA9552 I2C LED blinker + * PCA955X I2C LED blinker and I/O expanders * * https://www.nxp.com/docs/en/application-note/AN264.pdf + * https://www.nxp.com/docs/en/data-sheet/PCA9552.pdf + * https://www.nxp.com/docs/en/data-sheet/PCA9555.pdf + * https://www.nxp.com/docs/en/data-sheet/PCA9535_PCA9535C.pdf * * Copyright (c) 2017-2018, IBM Corporation. * Copyright (c) 2020 Philippe Mathieu-Daudé @@ -12,9 +15,9 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/module.h" #include "qemu/bitops.h" #include "hw/core/qdev-properties.h" +#include "hw/i2c/i2c.h" #include "hw/gpio/pca9552.h" #include "hw/gpio/pca9552_regs.h" #include "hw/core/irq.h" @@ -24,6 +27,25 @@ #include "trace.h" #include "qom/object.h" +#define PCA955X_NR_REGS 10 +#define PCA955X_PIN_COUNT_MAX 16 + +OBJECT_DECLARE_TYPE(PCA955xState, PCA955xClass, PCA955X) + +struct PCA955xState { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + + uint8_t len; + uint8_t pointer; + + uint8_t regs[PCA955X_NR_REGS]; + qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX]; + uint8_t ext_state[PCA955X_PIN_COUNT_MAX]; + char *description; /* For debugging purpose only */ +}; + struct PCA955xClass { /*< private >*/ I2CSlaveClass parent_class; @@ -33,10 +55,7 @@ struct PCA955xClass { uint8_t max_reg; bool has_led_support; }; -typedef struct PCA955xClass PCA955xClass; -DECLARE_CLASS_CHECKERS(PCA955xClass, PCA955X, - TYPE_PCA955X) /* * Note: The LED_ON and LED_OFF configuration values for the PCA955X * chips are the reverse of the PCA953X family of chips. diff --git a/include/hw/gpio/pca9552.h b/include/hw/gpio/pca9552.h index 43b175235d..71479ea000 100644 --- a/include/hw/gpio/pca9552.h +++ b/include/hw/gpio/pca9552.h @@ -1,39 +1,17 @@ /* - * PCA9552 I2C LED blinker + * PCA955X I2C LED blinker and I/O expanders * * Copyright (c) 2017-2018, IBM Corporation. * * This work is licensed under the terms of the GNU GPL, version 2 or * later. See the COPYING file in the top-level directory. */ -#ifndef PCA9552_H -#define PCA9552_H -#include "hw/i2c/i2c.h" -#include "qom/object.h" +#ifndef HW_GPIO_PCA9552_H +#define HW_GPIO_PCA9552_H -#define TYPE_PCA9552 "pca9552" #define TYPE_PCA955X "pca955x" +#define TYPE_PCA9552 "pca9552" #define TYPE_PCA9535 "pca9535" -typedef struct PCA955xState PCA955xState; -DECLARE_INSTANCE_CHECKER(PCA955xState, PCA955X, - TYPE_PCA955X) - -#define PCA955X_NR_REGS 10 -#define PCA955X_PIN_COUNT_MAX 16 - -struct PCA955xState { - /*< private >*/ - I2CSlave i2c; - /*< public >*/ - - uint8_t len; - uint8_t pointer; - - uint8_t regs[PCA955X_NR_REGS]; - qemu_irq gpio_out[PCA955X_PIN_COUNT_MAX]; - uint8_t ext_state[PCA955X_PIN_COUNT_MAX]; - char *description; /* For debugging purpose only */ -}; #endif From 4618d0465a131a3b91a8d5d860cf18191deee8f2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:41 +0200 Subject: [PATCH 341/860] hw/gpio: pca9552: rename I2CSlave member to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the conventional parent_obj name for the embedded I2CSlave instance. Signed-off-by: Emmanuel Blot Signed-off-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-3-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 36bc4f86ed..da21e56aab 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -34,7 +34,7 @@ OBJECT_DECLARE_TYPE(PCA955xState, PCA955xClass, PCA955X) struct PCA955xState { /*< private >*/ - I2CSlave i2c; + I2CSlave parent_obj; /*< public >*/ uint8_t len; @@ -401,7 +401,7 @@ static const VMStateDescription pca9552_vmstate = { VMSTATE_UINT8(pointer, PCA955xState), VMSTATE_UINT8_ARRAY(regs, PCA955xState, PCA955X_NR_REGS), VMSTATE_UINT8_ARRAY(ext_state, PCA955xState, PCA955X_PIN_COUNT_MAX), - VMSTATE_I2C_SLAVE(i2c, PCA955xState), + VMSTATE_I2C_SLAVE(parent_obj, PCA955xState), VMSTATE_END_OF_LIST() } }; From 520ea63bd72d0b64fc150c92f48d5ce322712954 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:42 +0200 Subject: [PATCH 342/860] hw/gpio: pca9552: default description to the instantiated type name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When no description is supplied, fall back to the actual QOM type name (pca9552 / pca9535 / pca9555) via object_get_typename() instead of the opaque "pca-unspecified" placeholder, matching the PCA9554 model and giving meaningful device labels in traces. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-4-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index da21e56aab..ffeb438d2a 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -488,7 +488,7 @@ static void pca955x_realize(DeviceState *dev, Error **errp) PCA955xState *s = PCA955X(dev); if (!s->description) { - s->description = g_strdup("pca-unspecified"); + s->description = g_strdup(object_get_typename(OBJECT(dev))); } qdev_init_gpio_out(dev, s->gpio_out, k->pin_count); From ee7042b317d9854d7ed221682ced4dba945dea04 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:43 +0200 Subject: [PATCH 343/860] hw/gpio: pca9552: declare pca9555 device as an alias of pca9535 device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCA9555 HW is mostly identical to PCA9535. PCA9555 HW features pull-up resistors that are not available on PCA9535. Pull-up are not handled by current PCA955x implementation and PCA9535 already initializes input as Hi-Z. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-5-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 9 +++++++-- include/hw/gpio/pca9552.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index ffeb438d2a..3b9b63eb17 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -523,7 +523,7 @@ static void pca9552_class_init(ObjectClass *oc, const void *data) pc->has_led_support = true; } -static void pca9535_class_init(ObjectClass *oc, const void *data) +static void pca95x5_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCA955xClass *pc = PCA955X_CLASS(oc); @@ -553,7 +553,12 @@ static const TypeInfo pca955x_types[] = { { .name = TYPE_PCA9535, .parent = TYPE_PCA955X, - .class_init = pca9535_class_init, + .class_init = pca95x5_class_init, + }, + { + .name = TYPE_PCA9555, + .parent = TYPE_PCA955X, + .class_init = pca95x5_class_init, } }; diff --git a/include/hw/gpio/pca9552.h b/include/hw/gpio/pca9552.h index 71479ea000..5299c13829 100644 --- a/include/hw/gpio/pca9552.h +++ b/include/hw/gpio/pca9552.h @@ -13,5 +13,6 @@ #define TYPE_PCA955X "pca955x" #define TYPE_PCA9552 "pca9552" #define TYPE_PCA9535 "pca9535" +#define TYPE_PCA9555 "pca9555" #endif From 0bfc57212e2ec42fd4b8da2d23b801224344304a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:44 +0200 Subject: [PATCH 344/860] hw/gpio: pca9552: use the Resettable interface instead of legacy reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the PCA9552 and PCA9535/PCA9555 reset handlers from the legacy device reset hook to the Resettable interface: move each reset body into a ResettableHoldPhase handler and register it through the class's ResettableClass::phases.hold instead of device_class_set_legacy_reset(). No functional change. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-6-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 3b9b63eb17..66f699bf6a 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -406,9 +406,9 @@ static const VMStateDescription pca9552_vmstate = { } }; -static void pca9552_reset(DeviceState *dev) +static void pca9552_reset_hold(Object *obj, ResetType type) { - PCA955xState *s = PCA955X(dev); + PCA955xState *s = PCA955X(obj); s->regs[PCA9552_PSC0] = 0xFF; s->regs[PCA9552_PWM0] = 0x80; @@ -426,9 +426,9 @@ static void pca9552_reset(DeviceState *dev) s->len = 0; } -static void pca9535_reset(DeviceState *dev) +static void pca9535_reset_hold(Object *obj, ResetType type) { - PCA955xState *s = PCA955X(dev); + PCA955xState *s = PCA955X(obj); s->regs[PCA9535_INPUT0] = 0xFF; /* All inputs high (pull-ups) */ s->regs[PCA9535_INPUT1] = 0xFF; /* All inputs high (pull-ups) */ @@ -514,9 +514,10 @@ static void pca955x_class_init(ObjectClass *klass, const void *data) static void pca9552_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); PCA955xClass *pc = PCA955X_CLASS(oc); - device_class_set_legacy_reset(dc, pca9552_reset); + rc->phases.hold = pca9552_reset_hold; dc->vmsd = &pca9552_vmstate; pc->max_reg = PCA9552_LS3; pc->pin_count = 16; @@ -526,9 +527,10 @@ static void pca9552_class_init(ObjectClass *oc, const void *data) static void pca95x5_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); PCA955xClass *pc = PCA955X_CLASS(oc); - device_class_set_legacy_reset(dc, pca9535_reset); + rc->phases.hold = pca9535_reset_hold; dc->vmsd = &pca9552_vmstate; pc->max_reg = PCA9535_CONFIG1; pc->pin_count = 16; From 469daf3eeff15d43c3d28d07555b4f134909b49e Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:45 +0200 Subject: [PATCH 345/860] hw/gpio: pca9552: apply input polarity inversion on read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA9535 polarity inversion register inverts the value read back from the input port for every pin, regardless of its direction, and does not affect the output drive or the physical pin level. Store the raw pin level in the input register and apply the polarity inversion when the input port is read, instead of XORing it into the stored value of output-configured pins only. The interrupt output now reflects the raw pin level, matching the datasheet. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-7-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 66f699bf6a..24f6465415 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -167,9 +167,12 @@ static void pca955x_update_pin_input(PCA955xState *s) /* PCA9535: Simple GPIO behavior */ uint8_t config_reg = PCA9535_CONFIG0 + (i / 8); uint8_t output_reg = PCA9535_OUTPUT0 + (i / 8); - uint8_t polarity_reg = PCA9535_POLARITY0 + (i / 8); - /* Check if pin is configured as input */ + /* + * The input register holds the raw pin logic level; the + * polarity inversion register is only applied when the input + * port is read (see pca955x_read()). + */ if (s->regs[config_reg] & bit_mask) { /* Input mode - reflect external state */ if (s->ext_state[i] == PCA9552_PIN_LOW) { @@ -179,12 +182,8 @@ static void pca955x_update_pin_input(PCA955xState *s) } } else { /* Output mode - reflect output register value */ - uint8_t output_bit = s->regs[output_reg] & bit_mask; - uint8_t polarity_bit = s->regs[polarity_reg] & bit_mask; - - /* Apply polarity inversion if set */ s->regs[input_reg] = (s->regs[input_reg] & ~bit_mask) | - ((output_bit ^ polarity_bit) & bit_mask); + (s->regs[output_reg] & bit_mask); } } @@ -206,6 +205,18 @@ static uint8_t pca955x_read(PCA955xState *s, uint8_t reg) return 0xFF; } + /* + * On the GPIO variants, reading an input port returns the raw pin + * levels XORed with the polarity inversion register, as specified by + * the datasheet. + */ + if (!k->has_led_support && + (reg == PCA9535_INPUT0 || reg == PCA9535_INPUT1)) { + uint8_t polarity_reg = PCA9535_POLARITY0 + (reg - PCA9535_INPUT0); + + return s->regs[reg] ^ s->regs[polarity_reg]; + } + return s->regs[reg]; } From f8e1f6488f2b9f79b37c9c40db17f9be1beea032 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:46 +0200 Subject: [PATCH 346/860] hw/gpio: pca9552: conform GPIO command handling to the datasheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA9535/PCA9555 GPIO expanders share the PCA955X command dispatch path with the PCA9552 LED blinker, but their register access differs from the LED variant: - Auto-increment happens on every access and toggles bit 0 so the pointer stays within the addressed register pair (input, output, polarity, config); there is no AI enable bit. - The command byte only decodes 3 bits, so addresses beyond the last register alias back into the 8-register window instead of faulting. Branch the auto-increment and command-decode logic on has_led_support so the GPIO variants follow their datasheet while the PCA9552 behaviour is left untouched. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-8-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 24f6465415..54a5945d92 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -259,14 +259,26 @@ static void pca955x_write(PCA955xState *s, uint8_t reg, uint8_t data) } /* - * When Auto-Increment is on, the register address is incremented - * after each byte is sent to or received by the device. The index - * rollovers to 0 when the maximum register address is reached. + * Advance the command pointer after each byte sent to or received from the + * device. + * + * The LED variant auto-increments only when the AI bit (bit 4) is set in the + * command byte, rolling over to 0 once the maximum register address is + * reached. + * + * The GPIO variants auto-increment on every access, toggling bit 0 so the + * pointer stays within the addressed register pair + * (input/output/polarity/config), as specified by their datasheet. */ static void pca955x_autoinc(PCA955xState *s) { PCA955xClass *k = PCA955X_GET_CLASS(s); + if (!k->has_led_support) { + s->pointer ^= 0x1; + return; + } + if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { uint8_t reg = s->pointer & 0xf; @@ -275,12 +287,25 @@ static void pca955x_autoinc(PCA955xState *s) } } +/* + * The LED variant addresses its registers with a 4-bit command field, while + * the GPIO variants only decode 3 bits (the command wraps into the 8-register + * window). + */ +static inline uint8_t pca955x_cmd_reg(PCA955xState *s) +{ + PCA955xClass *k = PCA955X_GET_CLASS(s); + + return s->pointer & (k->has_led_support ? 0xf : 0x7); +} + static uint8_t pca955x_recv(I2CSlave *i2c) { PCA955xState *s = PCA955X(i2c); + PCA955xClass *k = PCA955X_GET_CLASS(s); uint8_t ret; - ret = pca955x_read(s, s->pointer & 0xf); + ret = pca955x_read(s, pca955x_cmd_reg(s)); /* * From the Specs: @@ -292,7 +317,7 @@ static uint8_t pca955x_recv(I2CSlave *i2c) * I don't know what should be done in this case, so throw an * error. */ - if (s->pointer == PCA9552_AUTOINC) { + if (k->has_led_support && s->pointer == PCA9552_AUTOINC) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Autoincrement read starting with register 0\n", __func__); @@ -312,7 +337,7 @@ static int pca955x_send(I2CSlave *i2c, uint8_t data) s->pointer = data; s->len++; } else { - pca955x_write(s, s->pointer & 0xf, data); + pca955x_write(s, pca955x_cmd_reg(s), data); pca955x_autoinc(s); } From a11babc63217a5e3e8a9a7350654e35755f6ad11 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:47 +0200 Subject: [PATCH 347/860] hw/gpio: pca9552: expose GPIO pins as pin%d QOM properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA9552 exposes its LED channels as led%d QOM string properties, but the GPIO variants (PCA9535/PCA9555) inherited the same led%d interface, which drives the LED selector registers and is meaningless for a plain I/O expander. Add pin%d string properties ("low"/"high") for the GPIO variants, mirroring the standalone pca9555 model: - reading returns the raw pin logic level from the INPUT register; - writing drives the external input level, but only for pins the guest has configured as inputs (writes to output pins are ignored with a LOG_UNIMP message). The LED variant keeps its led%d properties. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-9-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9552.c | 91 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 54a5945d92..719149b717 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -68,6 +68,7 @@ struct PCA955xClass { #define PCA9552_PIN_HIZ 0x1 static const char *led_state[] = {"on", "off", "pwm0", "pwm1"}; +static const char *pin_state[] = {"low", "high"}; static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin) { @@ -428,6 +429,79 @@ static void pca955x_set_led(Object *obj, Visitor *v, const char *name, pca955x_write(s, reg, val); } +static void pca955x_set_ext_state(PCA955xState *s, int pin, int level); + +static void pca955x_get_pin(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA955xClass *k = PCA955X_GET_CLASS(obj); + PCA955xState *s = PCA955X(obj); + int pin, rc; + uint8_t input_reg, state; + + rc = sscanf(name, "pin%2d", &pin); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (pin < 0 || pin >= k->pin_count) { + error_setg(errp, "%s invalid pin %s", __func__, name); + return; + } + + /* + * Report the raw pin logic level; polarity inversion is a read-time + * transform applied to the INPUT register, not to the pin state itself. + */ + input_reg = PCA9535_INPUT0 + (pin / 8); + state = (s->regs[input_reg] >> (pin % 8)) & 0x1; + visit_type_str(v, name, (char **)&pin_state[state], errp); +} + +static void pca955x_set_pin(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PCA955xClass *k = PCA955X_GET_CLASS(obj); + PCA955xState *s = PCA955X(obj); + int pin, rc; + uint8_t state, config_reg; + g_autofree char *state_str = NULL; + + if (!visit_type_str(v, name, &state_str, errp)) { + return; + } + rc = sscanf(name, "pin%2d", &pin); + if (rc != 1) { + error_setg(errp, "%s: error reading %s", __func__, name); + return; + } + if (pin < 0 || pin >= k->pin_count) { + error_setg(errp, "%s invalid pin %s", __func__, name); + return; + } + + for (state = 0; state < ARRAY_SIZE(pin_state); state++) { + if (!strcmp(state_str, pin_state[state])) { + break; + } + } + if (state >= ARRAY_SIZE(pin_state)) { + error_setg(errp, "%s invalid pin state %s", __func__, state_str); + return; + } + + /* Only input-configured pins can be driven by an external device. */ + config_reg = PCA9535_CONFIG0 + (pin / 8); + if (!((s->regs[config_reg] >> (pin % 8)) & 0x1)) { + qemu_log_mask(LOG_UNIMP, + "%s: pin %d is configured as output, ignoring set\n", + s->description, pin); + return; + } + + pca955x_set_ext_state(s, pin, state != PCA9552_PIN_LOW); +} + static const VMStateDescription pca9552_vmstate = { .name = "PCA9552", .version_id = 0, @@ -485,15 +559,22 @@ static void pca9535_reset_hold(Object *obj, ResetType type) static void pca955x_initfn(Object *obj) { PCA955xClass *k = PCA955X_GET_CLASS(obj); - int led; assert(k->pin_count <= PCA955X_PIN_COUNT_MAX); - for (led = 0; led < k->pin_count; led++) { + for (int ix = 0; ix < k->pin_count; ix++) { char *name; - name = g_strdup_printf("led%d", led); - object_property_add(obj, name, "bool", pca955x_get_led, pca955x_set_led, - NULL, NULL); + if (k->has_led_support) { + /* LED variant: expose the LED selector state as led%d. */ + name = g_strdup_printf("led%d", ix); + object_property_add(obj, name, "bool", + pca955x_get_led, pca955x_set_led, NULL, NULL); + } else { + /* GPIO variant: expose the pin logic level as pin%d. */ + name = g_strdup_printf("pin%d", ix); + object_property_add(obj, name, "str", + pca955x_get_pin, pca955x_set_pin, NULL, NULL); + } g_free(name); } } From 71aa15068ccaa8b330f4f324c33315557d54742c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:48 +0200 Subject: [PATCH 348/860] tests/qtest: add PCA9555 register access tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a qtest for the PCA9555 16-bit I/O port expander. This first set covers the power-on reset defaults and the read/write behaviour of the OUTPUT, CONFIG and POLARITY register pairs. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-10-814575bc076b@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/meson.build | 1 + tests/qtest/pca9555-test.c | 44 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 tests/qtest/pca9555-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5e1fc597e6..e92130bd04 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -319,6 +319,7 @@ qos_test_ss.add( 'tulip-test.c', 'nvme-test.c', 'pca9552-test.c', + 'pca9555-test.c', 'pci-test.c', 'pcnet-test.c', 'rs5c372-test.c', diff --git a/tests/qtest/pca9555-test.c b/tests/qtest/pca9555-test.c new file mode 100644 index 0000000000..5945c3441e --- /dev/null +++ b/tests/qtest/pca9555-test.c @@ -0,0 +1,44 @@ +/* + * QTest testcase for the PCA9555 16-bit I/O port expander + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/gpio/pca9552_regs.h" +#include "libqos/i2c.h" +#include "libqos/qgraph.h" + +#define PCA9555_TEST_ADDR 0x20 + +/* Verify power-on reset defaults match the PCA9555 datasheet. */ +static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_POLARITY0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_POLARITY1), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG1), ==, 0xFF); +} + +static void pca9555_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "address=0x20" + }; + add_qi2c_address(&opts, &(QI2CAddress) { PCA9555_TEST_ADDR }); + + qos_node_create_driver("pca9555", i2c_device_create); + qos_node_consumes("pca9555", "i2c-bus", &opts); + + qos_add_test("reset-defaults", "pca9555", test_reset_defaults, NULL); +} + +libqos_init(pca9555_register_nodes); From 0e964386792dcd40e6657155db8545a814eefa4b Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:03 +0200 Subject: [PATCH 349/860] tests/qtest: pca9555: test output-to-input reflection and pull-ups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests covering the pin I/O semantics of the expander: output-driven pins reflected in the input register, the pull-up seen on input-configured pins, and the independence of the two 8-bit ports. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-11-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9555-test.c | 75 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/qtest/pca9555-test.c b/tests/qtest/pca9555-test.c index 5945c3441e..6b085702c3 100644 --- a/tests/qtest/pca9555-test.c +++ b/tests/qtest/pca9555-test.c @@ -28,6 +28,77 @@ static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG1), ==, 0xFF); } +/* + * When a pin is configured as output and driven low (output=0, config=0), + * the input register should reflect 0 for that pin. + * When driven high (output=1, config=0), input should reflect 1. + * When configured as input (config=1), PCA5555 pull-up makes it read 1. + */ +static void test_output_drives_input(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_CONFIG0, 0xF0); + i2c_set8(dev, PCA9535_OUTPUT0, 0xFA); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFA); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + + i2c_set8(dev, PCA9535_OUTPUT0, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); +} + +/* + * When all pins are inputs (config=0xFF) and no external driver, + * PCA9555 pull-ups should make the input register read all ones. + * Switching a pin to output mode with output=0 should drive it low. + */ +static void test_input_pullup(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_OUTPUT0, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); +} + +/* + * Test that both ports are independent: changing port 0 registers + * should not affect port 1 and vice versa. + */ +static void test_port_independence(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0x00); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG1), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_CONFIG1, 0x00); + i2c_set8(dev, PCA9535_OUTPUT1, 0xAA); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xAA); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0xAA); +} + static void pca9555_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -39,6 +110,10 @@ static void pca9555_register_nodes(void) qos_node_consumes("pca9555", "i2c-bus", &opts); qos_add_test("reset-defaults", "pca9555", test_reset_defaults, NULL); + qos_add_test("output-drives-input", "pca9555", test_output_drives_input, + NULL); + qos_add_test("input-pullup", "pca9555", test_input_pullup, NULL); + qos_add_test("port-independence", "pca9555", test_port_independence, NULL); } libqos_init(pca9555_register_nodes); From f2fa57afffde5885d69d2db3a7a22a8c5d1eba1a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:04 +0200 Subject: [PATCH 350/860] tests/qtest: pca9555: test polarity inversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests for the polarity inversion register: the inversion is applied when reading the INPUT register, both for input pins (pull-up) and for output-driven pins, and it does not affect the OUTPUT register readback. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-12-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9555-test.c | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/qtest/pca9555-test.c b/tests/qtest/pca9555-test.c index 6b085702c3..2afa061bd2 100644 --- a/tests/qtest/pca9555-test.c +++ b/tests/qtest/pca9555-test.c @@ -99,6 +99,46 @@ static void test_port_independence(void *obj, void *data, g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0xAA); } +/* + * Polarity inversion: reading INPUT with polarity bits set should + * return the XOR of the actual input state and the polarity register. + */ +static void test_polarity_inversion(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xFF); + + i2c_set8(dev, PCA9535_POLARITY0, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x00); + + i2c_set8(dev, PCA9535_POLARITY0, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xF0); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0xFF); + + i2c_set8(dev, PCA9535_POLARITY1, 0xAA); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT1), ==, 0x55); +} + +/* Polarity inversion combined with output-driven pins. */ +static void test_polarity_with_output(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0xA5); + + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0xA5); + + i2c_set8(dev, PCA9535_POLARITY0, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9535_INPUT0), ==, 0x5A); + + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0xA5); +} + static void pca9555_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -114,6 +154,10 @@ static void pca9555_register_nodes(void) NULL); qos_add_test("input-pullup", "pca9555", test_input_pullup, NULL); qos_add_test("port-independence", "pca9555", test_port_independence, NULL); + qos_add_test("polarity-inversion", "pca9555", test_polarity_inversion, + NULL); + qos_add_test("polarity-with-output", "pca9555", test_polarity_with_output, + NULL); } libqos_init(pca9555_register_nodes); From f43ee338662b02827c61c033fcc35be68eae952c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:05 +0200 Subject: [PATCH 351/860] tests/qtest: pca9555: test auto-increment and command wrapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add tests for the I2C command protocol of the GPIO variant: the auto-increment that toggles bit 0 within a register pair on reads and writes, and the 3-bit command wrapping that aliases out-of-range register addresses back into the register window. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-13-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9555-test.c | 88 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/tests/qtest/pca9555-test.c b/tests/qtest/pca9555-test.c index 2afa061bd2..84d771bcbb 100644 --- a/tests/qtest/pca9555-test.c +++ b/tests/qtest/pca9555-test.c @@ -139,6 +139,87 @@ static void test_polarity_with_output(void *obj, void *data, g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0xA5); } +/* + * The PCA9555 auto-increments by toggling bit 0 of the command pointer + * within a register pair. Reading two bytes from INPUT0 should yield + * INPUT0 then INPUT1. + */ +static void test_auto_increment_read(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + i2c_set8(dev, PCA9535_CONFIG0, 0x00); + i2c_set8(dev, PCA9535_CONFIG1, 0x00); + i2c_set8(dev, PCA9535_OUTPUT0, 0xAA); + i2c_set8(dev, PCA9535_OUTPUT1, 0x55); + + i2c_read_block(dev, PCA9535_INPUT0, buf, 2); + g_assert_cmphex(buf[0], ==, 0xAA); + g_assert_cmphex(buf[1], ==, 0x55); + + i2c_read_block(dev, PCA9535_OUTPUT0, buf, 2); + g_assert_cmphex(buf[0], ==, 0xAA); + g_assert_cmphex(buf[1], ==, 0x55); +} + +/* + * Auto-increment write: writing two data bytes after a command byte + * should write to port 0 then port 1 of the addressed register pair. + */ +static void test_auto_increment_write(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + buf[0] = 0x12; + buf[1] = 0x34; + i2c_write_block(dev, PCA9535_OUTPUT0, buf, 2); + + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT0), ==, 0x12); + g_assert_cmphex(i2c_get8(dev, PCA9535_OUTPUT1), ==, 0x34); + + buf[0] = 0x0F; + buf[1] = 0xF0; + i2c_write_block(dev, PCA9535_CONFIG0, buf, 2); + + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG0), ==, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9535_CONFIG1), ==, 0xF0); +} + +/* + * Auto-increment toggles within the pair: starting from port 1 should + * wrap back to port 0 (toggle bit 0). + */ +static void test_auto_increment_toggle(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + i2c_set8(dev, PCA9535_OUTPUT0, 0xAA); + i2c_set8(dev, PCA9535_OUTPUT1, 0x55); + + i2c_read_block(dev, PCA9535_OUTPUT1, buf, 2); + g_assert_cmphex(buf[0], ==, 0x55); + g_assert_cmphex(buf[1], ==, 0xAA); +} + +/* + * Verify the command byte wraps at 3 bits: register addresses + * beyond 7 should alias to the same register (bits [2:0] only). + */ +static void test_command_wrapping(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9535_OUTPUT0, 0x42); + + g_assert_cmphex(i2c_get8(dev, 0x0A), ==, 0x42); +} + static void pca9555_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -158,6 +239,13 @@ static void pca9555_register_nodes(void) NULL); qos_add_test("polarity-with-output", "pca9555", test_polarity_with_output, NULL); + qos_add_test("auto-increment-read", "pca9555", test_auto_increment_read, + NULL); + qos_add_test("auto-increment-write", "pca9555", test_auto_increment_write, + NULL); + qos_add_test("auto-increment-toggle", "pca9555", test_auto_increment_toggle, + NULL); + qos_add_test("command-wrapping", "pca9555", test_command_wrapping, NULL); } libqos_init(pca9555_register_nodes); From f41bc13152c29261202c1394e3429188ac6c0fdd Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:06 +0200 Subject: [PATCH 352/860] tests/qtest: pca9552: test behaviour specific to the LED variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA9552 shares its device model with the PCA9535/PCA9555 GPIO expanders but decodes registers differently. Add tests for the behaviour that is specific to the LED variant and diverges from the PCA9555: - the power-on reset defaults of the prescaler, PWM and LED-selector registers; - the prescaler/PWM registers (2-5), which are OUTPUT/POLARITY on the PCA9555, as plain read/write storage; - the auto-increment, which only advances when the AI command bit is set and wraps modulo the full 10-register map (rather than toggling bit 0 within a register pair); - the 4-bit command decode, where an out-of-range register reads back 0xFF instead of aliasing into the register window. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-14-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9552-test.c | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/qtest/pca9552-test.c b/tests/qtest/pca9552-test.c index 7474957692..3718dfbd22 100644 --- a/tests/qtest/pca9552-test.c +++ b/tests/qtest/pca9552-test.c @@ -77,6 +77,76 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0xEF); } +/* Verify the power-on reset defaults. */ +static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + /* Prescalers, PWM duty cycles and LED selectors (all LEDs off) */ + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC0), ==, 0xFF); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM0), ==, 0x80); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC1), ==, 0xFF); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM1), ==, 0x80); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS0), ==, 0x55); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS1), ==, 0x55); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS2), ==, 0x55); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS3), ==, 0x55); + + /* All LEDs off, so every pin floats high through its pull-up */ + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT0), ==, 0xFF); + g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT1), ==, 0xFF); +} + +/* + * The PCA9552 only advances the command pointer when the AI bit is set, and + * it wraps modulo the full 10-register map. + */ +static void test_autoinc_requires_ai_bit(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + uint8_t reg; + uint8_t resp; + + /* + * With the AI bit, reading from LS3 (register 9) rolls over to INPUT0 + * (register 0), not to a sibling in a register pair. All LEDs are off + * after reset so the input ports read 0xFF. + */ + reg = PCA9552_LS3 | PCA9552_AUTOINC; + qi2c_send(i2cdev, ®, 1); + qi2c_recv(i2cdev, &resp, 1); /* LS3 */ + g_assert_cmphex(resp, ==, 0x55); + qi2c_recv(i2cdev, &resp, 1); /* wraps to INPUT0 */ + g_assert_cmphex(resp, ==, 0xFF); + qi2c_recv(i2cdev, &resp, 1); /* INPUT1 */ + g_assert_cmphex(resp, ==, 0xFF); + + /* + * Without the AI bit the pointer must not advance: repeated reads keep + * returning the same register. + */ + i2c_set8(i2cdev, PCA9552_LS0, 0x54); + reg = PCA9552_LS0; + qi2c_send(i2cdev, ®, 1); + qi2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); + qi2c_recv(i2cdev, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); +} + +/* + * The PCA9552 decodes a 4-bit command and has no register past LS3 (9), so + * addressing register 0x0A reads back 0xFF. + */ +static void test_command_out_of_range(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(i2cdev, 0x0A), ==, 0xFF); +} + static void pca9552_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -89,5 +159,11 @@ static void pca9552_register_nodes(void) qos_add_test("tx-rx", "pca9552", send_and_receive, NULL); qos_add_test("rx-autoinc", "pca9552", receive_autoinc, NULL); + qos_add_test("reset-defaults", "pca9552", test_reset_defaults, NULL); + qos_add_test("autoinc-requires-ai-bit", "pca9552", + test_autoinc_requires_ai_bit, NULL); + qos_add_test("command-out-of-range", "pca9552", test_command_out_of_range, + NULL); } + libqos_init(pca9552_register_nodes); From 5eceb48bbe86f4ef5fa59aa9896d2b09d5aae11a Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:07 +0200 Subject: [PATCH 353/860] hw/gpio: pca9554: add PCA9536 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA9536 is a 4-bit I/O expander that's register-compatible with the PCA9554 but only has 4 pins. Rather than duplicating the whole driver, make the existing PCA9554 model parameterizable and register PCA9536 as a subtype. Introduce a PCA9554Class with a pin_count property, and replace every hard-coded PCA9554_PIN_COUNT reference in the driver with the class value. The reset function now computes the correct pin mask from pin_count instead of assuming 0xFF. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-15-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9554.c | 81 +++++++++++++++++++++++++-------------- include/hw/gpio/pca9554.h | 2 + 2 files changed, 55 insertions(+), 28 deletions(-) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index 8427e01e9b..b44ec0d999 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -24,6 +24,8 @@ struct PCA9554Class { /*< private >*/ I2CSlaveClass parent_class; /*< public >*/ + + uint8_t pin_count; }; typedef struct PCA9554Class PCA9554Class; @@ -37,12 +39,13 @@ static const char *pin_state[] = {"low", "high"}; static void pca9554_update_pin_input(PCA9554State *s) { + PCA9554Class *pc = PCA9554_GET_CLASS(s); int i; uint8_t config = s->regs[PCA9554_CONFIG]; uint8_t output = s->regs[PCA9554_OUTPUT]; uint8_t internal_state = config | output; - for (i = 0; i < PCA9554_PIN_COUNT; i++) { + for (i = 0; i < pc->pin_count; i++) { uint8_t bit_mask = 1 << i; uint8_t internal_pin_state = (internal_state >> i) & 0x1; uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; @@ -67,7 +70,7 @@ static void pca9554_update_pin_input(PCA9554State *s) break; } - /* update irq state only if pin state changed */ + /* drive the per-pin GPIO output only if the pin level changed */ new_value = s->regs[PCA9554_INPUT] & bit_mask; if (new_value != old_value) { if (new_value) { @@ -99,6 +102,12 @@ static uint8_t pca9554_read(PCA9554State *s, uint8_t reg) static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data) { + PCA9554Class *pc = PCA9554_GET_CLASS(s); + uint8_t pin_mask = (1 << pc->pin_count) - 1; + + /* Variants narrower than 8 bits ignore the unimplemented upper pins. */ + data &= pin_mask; + switch (reg) { case PCA9554_OUTPUT: case PCA9554_CONFIG: @@ -157,7 +166,7 @@ static void pca9554_get_pin(Object *obj, Visitor *v, const char *name, error_setg(errp, "%s: error reading %s", __func__, name); return; } - if (pin < 0 || pin >= PCA9554_PIN_COUNT) { + if (pin < 0 || pin >= PCA9554_GET_CLASS(s)->pin_count) { error_setg(errp, "%s invalid pin %s", __func__, name); return; } @@ -184,7 +193,7 @@ static void pca9554_set_pin(Object *obj, Visitor *v, const char *name, error_setg(errp, "%s: error reading %s", __func__, name); return; } - if (pin < 0 || pin >= PCA9554_PIN_COUNT) { + if (pin < 0 || pin >= PCA9554_GET_CLASS(s)->pin_count) { error_setg(errp, "%s invalid pin %s", __func__, name); return; } @@ -232,13 +241,15 @@ static const VMStateDescription pca9554_vmstate = { static void pca9554_reset(DeviceState *dev) { PCA9554State *s = PCA9554(dev); + PCA9554Class *pc = PCA9554_GET_CLASS(s); + uint8_t pin_mask = (1 << pc->pin_count) - 1; - s->regs[PCA9554_INPUT] = 0xFF; - s->regs[PCA9554_OUTPUT] = 0xFF; + s->regs[PCA9554_INPUT] = pin_mask; + s->regs[PCA9554_OUTPUT] = pin_mask; s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */ - s->regs[PCA9554_CONFIG] = 0xFF; /* All pins are inputs */ + s->regs[PCA9554_CONFIG] = pin_mask; /* All pins are inputs */ - memset(s->ext_state, PCA9554_PIN_HIZ, PCA9554_PIN_COUNT); + memset(s->ext_state, PCA9554_PIN_HIZ, pc->pin_count); pca9554_update_pin_input(s); s->pointer = 0x0; @@ -247,9 +258,10 @@ static void pca9554_reset(DeviceState *dev) static void pca9554_initfn(Object *obj) { + PCA9554Class *pc = PCA9554_GET_CLASS(obj); int pin; - for (pin = 0; pin < PCA9554_PIN_COUNT; pin++) { + for (pin = 0; pin < pc->pin_count; pin++) { char *name; name = g_strdup_printf("pin%d", pin); @@ -269,23 +281,24 @@ static void pca9554_set_ext_state(PCA9554State *s, int pin, int level) static void pca9554_gpio_in_handler(void *opaque, int pin, int level) { - PCA9554State *s = PCA9554(opaque); + PCA9554Class *pc = PCA9554_GET_CLASS(s); - assert((pin >= 0) && (pin < PCA9554_PIN_COUNT)); + assert((pin >= 0) && (pin < pc->pin_count)); pca9554_set_ext_state(s, pin, level); } static void pca9554_realize(DeviceState *dev, Error **errp) { PCA9554State *s = PCA9554(dev); + PCA9554Class *pc = PCA9554_GET_CLASS(s); if (!s->description) { - s->description = g_strdup("pca9554"); + s->description = g_strdup(object_get_typename(OBJECT(dev))); } - qdev_init_gpio_out(dev, s->gpio_out, PCA9554_PIN_COUNT); - qdev_init_gpio_in(dev, pca9554_gpio_in_handler, PCA9554_PIN_COUNT); + qdev_init_gpio_out(dev, s->gpio_out, pc->pin_count); + qdev_init_gpio_in(dev, pca9554_gpio_in_handler, pc->pin_count); } static const Property pca9554_properties[] = { @@ -296,6 +309,7 @@ static void pca9554_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + PCA9554Class *pc = PCA9554_CLASS(klass); k->event = pca9554_event; k->recv = pca9554_recv; @@ -304,21 +318,32 @@ static void pca9554_class_init(ObjectClass *klass, const void *data) device_class_set_legacy_reset(dc, pca9554_reset); dc->vmsd = &pca9554_vmstate; device_class_set_props(dc, pca9554_properties); + + pc->pin_count = PCA9554_PIN_COUNT; } -static const TypeInfo pca9554_info = { - .name = TYPE_PCA9554, - .parent = TYPE_I2C_SLAVE, - .instance_init = pca9554_initfn, - .instance_size = sizeof(PCA9554State), - .class_init = pca9554_class_init, - .class_size = sizeof(PCA9554Class), - .abstract = false, +static void pca9536_class_init(ObjectClass *klass, const void *data) +{ + PCA9554Class *pc = PCA9554_CLASS(klass); + + pc->pin_count = PCA9536_PIN_COUNT; +} + +static const TypeInfo pca9554_types[] = { + { + .name = TYPE_PCA9554, + .parent = TYPE_I2C_SLAVE, + .instance_init = pca9554_initfn, + .instance_size = sizeof(PCA9554State), + .class_init = pca9554_class_init, + .class_size = sizeof(PCA9554Class), + .abstract = false, + }, + { + .name = TYPE_PCA9536, + .parent = TYPE_PCA9554, + .class_init = pca9536_class_init, + } }; -static void pca9554_register_types(void) -{ - type_register_static(&pca9554_info); -} - -type_init(pca9554_register_types) +DEFINE_TYPES(pca9554_types); diff --git a/include/hw/gpio/pca9554.h b/include/hw/gpio/pca9554.h index 54bfc4c4c7..c09108e8b6 100644 --- a/include/hw/gpio/pca9554.h +++ b/include/hw/gpio/pca9554.h @@ -12,12 +12,14 @@ #include "qom/object.h" #define TYPE_PCA9554 "pca9554" +#define TYPE_PCA9536 "pca9536" typedef struct PCA9554State PCA9554State; DECLARE_INSTANCE_CHECKER(PCA9554State, PCA9554, TYPE_PCA9554) #define PCA9554_NR_REGS 4 #define PCA9554_PIN_COUNT 8 +#define PCA9536_PIN_COUNT 4 struct PCA9554State { /*< private >*/ From 0d02f7b87789f95393dcaa4b9109e29a061ae120 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:08 +0200 Subject: [PATCH 354/860] hw/gpio: pca9554: add hw-dir property honoring the configured pin direction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pinN QOM accessors are meant to let external agents observe and stimulate the expander's pins, but their default behaviour does not match real hardware: - to "drive" a pin, set_pin writes the OUTPUT register and then clears the pin's Configuration bit to force it into output mode. On a real device the pin direction is owned solely by the host (programmed through the Configuration register over I2C); an external agent can neither flip a pin's direction nor impose a level on a pin the host drives as an output -- the latter is a voltage conflict, not a legal operation. - get_pin returns a CONFIG|OUTPUT composite, i.e. the guest's intent, rather than the level actually sampled on the pin. The PCA9555 GPIO variant (hw/gpio/pca9552.c) already models this correctly and unconditionally: only input-configured pins can be driven from outside, and reads return the sampled INPUT register. Add a "hw-dir" property to bring the pca9554 pin accessors in line with the hardware (and with the PCA9555 model), without changing the behaviour seen by existing users: - hw-dir=true: set_pin only drives pins the guest has configured as inputs; a set on an output pin is refused with a LOG_UNIMP warning. get_pin returns the sampled INPUT register. - hw-dir=false (default): keeps the legacy, non-conformant behaviour for backward compatibility. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-16-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9554.c | 65 +++++++++++++++++++++++++-------------- include/hw/gpio/pca9554.h | 1 + 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index b44ec0d999..ed38fe102b 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -154,6 +154,14 @@ static int pca9554_event(I2CSlave *i2c, enum i2c_event event) return 0; } +static void pca9554_set_ext_state(PCA9554State *s, int pin, int level) +{ + if (s->ext_state[pin] != level) { + s->ext_state[pin] = level; + pca9554_update_pin_input(s); + } +} + static void pca9554_get_pin(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -171,9 +179,13 @@ static void pca9554_get_pin(Object *obj, Visitor *v, const char *name, return; } - state = pca9554_read(s, PCA9554_CONFIG); - state |= pca9554_read(s, PCA9554_OUTPUT); - state = (state >> pin) & 0x1; + /* + * Report the physical pin level. The input register is kept in sync by + * pca9554_update_pin_input(): output pins mirror the OUTPUT register and + * input pins reflect the externally driven (or pulled-up) level, so it + * holds the wire level regardless of the configured direction. + */ + state = (s->regs[PCA9554_INPUT] >> pin) & 0x1; visit_type_str(v, name, (char **)&pin_state[state], errp); } @@ -208,20 +220,34 @@ static void pca9554_set_pin(Object *obj, Visitor *v, const char *name, return; } - /* First, modify the output register bit */ - val = pca9554_read(s, PCA9554_OUTPUT); - mask = 0x1 << pin; - if (state == PCA9554_PIN_LOW) { - val &= ~(mask); + if (s->hw_dir) { + /* Warn and ignore if the guest has configured this pin as output */ + if (!((s->regs[PCA9554_CONFIG] >> pin) & 0x1)) { + qemu_log_mask(LOG_UNIMP, + "%s: pin %d is configured as output, " + "ignoring external set\n", + s->description, pin); + return; + } + /* Drive the external input level */ + pca9554_set_ext_state(s, pin, state != PCA9554_PIN_LOW); } else { - val |= mask; - } - pca9554_write(s, PCA9554_OUTPUT, val); + /* Legacy behavior: force output mode and drive */ + /* First, modify the output register bit */ + val = pca9554_read(s, PCA9554_OUTPUT); + mask = 0x1 << pin; + if (state == PCA9554_PIN_LOW) { + val &= ~(mask); + } else { + val |= mask; + } + pca9554_write(s, PCA9554_OUTPUT, val); - /* Then, clear the config register bit for output mode */ - val = pca9554_read(s, PCA9554_CONFIG); - val &= ~mask; - pca9554_write(s, PCA9554_CONFIG, val); + /* Then, clear the config register bit for output mode */ + val = pca9554_read(s, PCA9554_CONFIG); + val &= ~mask; + pca9554_write(s, PCA9554_CONFIG, val); + } } static const VMStateDescription pca9554_vmstate = { @@ -271,14 +297,6 @@ static void pca9554_initfn(Object *obj) } } -static void pca9554_set_ext_state(PCA9554State *s, int pin, int level) -{ - if (s->ext_state[pin] != level) { - s->ext_state[pin] = level; - pca9554_update_pin_input(s); - } -} - static void pca9554_gpio_in_handler(void *opaque, int pin, int level) { PCA9554State *s = PCA9554(opaque); @@ -303,6 +321,7 @@ static void pca9554_realize(DeviceState *dev, Error **errp) static const Property pca9554_properties[] = { DEFINE_PROP_STRING("description", PCA9554State, description), + DEFINE_PROP_BOOL("hw-dir", PCA9554State, hw_dir, false), }; static void pca9554_class_init(ObjectClass *klass, const void *data) diff --git a/include/hw/gpio/pca9554.h b/include/hw/gpio/pca9554.h index c09108e8b6..ac835371aa 100644 --- a/include/hw/gpio/pca9554.h +++ b/include/hw/gpio/pca9554.h @@ -33,6 +33,7 @@ struct PCA9554State { qemu_irq gpio_out[PCA9554_PIN_COUNT]; uint8_t ext_state[PCA9554_PIN_COUNT]; char *description; /* For debugging purpose only */ + bool hw_dir; /* Honor pin direction */ }; #endif From ed8e011b922c5ae23804d6dccbde1152af3a5381 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:09 +0200 Subject: [PATCH 355/860] hw/gpio: pca9554: reflect push-pull outputs in the input register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pca9554_update_pin_input() derived the pin level from CONFIG | OUTPUT, which treated an output driven high as Hi-Z and let ext_state pull it low. The PCA9554/PCA9536 output stage is push-pull, so a pin configured as an output drives the OUTPUT register level regardless of any external agent. Reflect the output value directly for output pins and keep the pull-up/ext_state behaviour for input pins, matching the PCA9555 GPIO variant. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-17-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- hw/gpio/pca9554.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index ed38fe102b..d4746719f9 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -43,43 +43,32 @@ static void pca9554_update_pin_input(PCA9554State *s) int i; uint8_t config = s->regs[PCA9554_CONFIG]; uint8_t output = s->regs[PCA9554_OUTPUT]; - uint8_t internal_state = config | output; for (i = 0; i < pc->pin_count; i++) { uint8_t bit_mask = 1 << i; - uint8_t internal_pin_state = (internal_state >> i) & 0x1; uint8_t old_value = s->regs[PCA9554_INPUT] & bit_mask; uint8_t new_value; - switch (internal_pin_state) { - case PCA9554_PIN_LOW: - s->regs[PCA9554_INPUT] &= ~bit_mask; - break; - case PCA9554_PIN_HIZ: + if (config & bit_mask) { /* - * pullup sets it to a logical 1 unless - * external device drives it low. + * Input: the pin is Hi-Z with a pull-up, so it reads high + * unless an external device drives it low. */ if (s->ext_state[i] == PCA9554_PIN_LOW) { s->regs[PCA9554_INPUT] &= ~bit_mask; } else { - s->regs[PCA9554_INPUT] |= bit_mask; + s->regs[PCA9554_INPUT] |= bit_mask; } - break; - default: - break; + } else { + /* Output: the push-pull stage drives the output register level. */ + s->regs[PCA9554_INPUT] = (s->regs[PCA9554_INPUT] & ~bit_mask) | + (output & bit_mask); } /* drive the per-pin GPIO output only if the pin level changed */ new_value = s->regs[PCA9554_INPUT] & bit_mask; if (new_value != old_value) { - if (new_value) { - /* changed from 0 to 1 */ - qemu_set_irq(s->gpio_out[i], 1); - } else { - /* changed from 1 to 0 */ - qemu_set_irq(s->gpio_out[i], 0); - } + qemu_set_irq(s->gpio_out[i], !!new_value); } } } From a52cf5a1826535f838d30be835613bf16f60fff5 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:10 +0200 Subject: [PATCH 356/860] hw/gpio: pca9554: expose pin%d as a string property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pinN properties are accessed with visit_type_str() (values "low" and "high"), but were registered as type "bool", so introspection advertised a boolean while the accessors require a string. Register them as "str", matching the PCA9555 GPIO variant. Fixes: de0c7d543bca ("misc: Add a pca9554 GPIO device model") Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-18-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- 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 d4746719f9..904698cdce 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -280,7 +280,7 @@ static void pca9554_initfn(Object *obj) char *name; name = g_strdup_printf("pin%d", pin); - object_property_add(obj, name, "bool", pca9554_get_pin, pca9554_set_pin, + object_property_add(obj, name, "str", pca9554_get_pin, pca9554_set_pin, NULL, NULL); g_free(name); } From ab707882d83b013521ab15d266ab41dbb6c9efbc Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:11 +0200 Subject: [PATCH 357/860] tests/qtest: add PCA9554 register access tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a qtest for the PCA9554 8-bit I/O port expander exercising the basic register access: power-on reset defaults and read/write of the OUTPUT, CONFIG and POLARITY registers. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-19-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/meson.build | 1 + tests/qtest/pca9554-test.c | 41 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tests/qtest/pca9554-test.c diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index e92130bd04..4d81857174 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -319,6 +319,7 @@ qos_test_ss.add( 'tulip-test.c', 'nvme-test.c', 'pca9552-test.c', + 'pca9554-test.c', 'pca9555-test.c', 'pci-test.c', 'pcnet-test.c', diff --git a/tests/qtest/pca9554-test.c b/tests/qtest/pca9554-test.c new file mode 100644 index 0000000000..a5f213a59c --- /dev/null +++ b/tests/qtest/pca9554-test.c @@ -0,0 +1,41 @@ +/* + * QTest testcase for the PCA9554/PCA9536 I/O port expanders + * + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.com) + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/gpio/pca9554_regs.h" +#include "libqos/i2c.h" +#include "libqos/qgraph.h" + +#define PCA9554_TEST_ADDR 0x20 + +/* Verify power-on reset defaults match the PCA9554 datasheet. */ +static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + /* All pins are inputs, pulled high, with no polarity inversion. */ + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0xFF); +} + +static void pca9554_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "address=0x20" + }; + add_qi2c_address(&opts, &(QI2CAddress) { PCA9554_TEST_ADDR }); + + qos_node_create_driver("pca9554", i2c_device_create); + qos_node_consumes("pca9554", "i2c-bus", &opts); + + qos_add_test("reset-defaults", "pca9554", test_reset_defaults, NULL); +} + +libqos_init(pca9554_register_nodes); From e5f73c36dbf0f52e1006d907d5ef283c9c01b52c Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:12 +0200 Subject: [PATCH 358/860] tests/qtest: pca9554: test output-to-input reflection and pull-ups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check that a pin configured as output drives its OUTPUT register level onto the pin (push-pull) as reflected by the INPUT register, and that a pin configured as input floats high through its pull-up. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-20-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9554-test.c | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/qtest/pca9554-test.c b/tests/qtest/pca9554-test.c index a5f213a59c..ce6e90f3f8 100644 --- a/tests/qtest/pca9554-test.c +++ b/tests/qtest/pca9554-test.c @@ -25,6 +25,48 @@ static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0xFF); } +/* + * A pin configured as output (config=0) drives its OUTPUT register level onto + * the pin (push-pull), which the INPUT register reflects. A pin configured as + * input (config=1) floats high through its pull-up. + */ +static void test_output_drives_input(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + /* Low nibble output, high nibble input (pull-up). */ + i2c_set8(dev, PCA9554_CONFIG, 0xF0); + i2c_set8(dev, PCA9554_OUTPUT, 0xFA); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFA); + + /* All outputs, driven low then high. */ + i2c_set8(dev, PCA9554_CONFIG, 0x00); + i2c_set8(dev, PCA9554_OUTPUT, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); + + i2c_set8(dev, PCA9554_OUTPUT, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); +} + +/* + * With all pins configured as inputs the pull-ups make the INPUT register read + * all ones regardless of the OUTPUT register; switching a pin to output with + * output=0 drives it low. + */ +static void test_input_pullup(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + + i2c_set8(dev, PCA9554_OUTPUT, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + + i2c_set8(dev, PCA9554_CONFIG, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); +} + static void pca9554_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -36,6 +78,9 @@ static void pca9554_register_nodes(void) qos_node_consumes("pca9554", "i2c-bus", &opts); qos_add_test("reset-defaults", "pca9554", test_reset_defaults, NULL); + qos_add_test("output-drives-input", "pca9554", test_output_drives_input, + NULL); + qos_add_test("input-pullup", "pca9554", test_input_pullup, NULL); } libqos_init(pca9554_register_nodes); From e1338eb6da3041be64c096ba60e8680dbcd51a68 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:13 +0200 Subject: [PATCH 359/860] tests/qtest: pca9554: test polarity inversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verify that the polarity register inverts the value read back from the INPUT register, both on pulled-up inputs and on output-driven pins, while leaving the OUTPUT register itself unchanged. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-21-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9554-test.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/qtest/pca9554-test.c b/tests/qtest/pca9554-test.c index ce6e90f3f8..74237df611 100644 --- a/tests/qtest/pca9554-test.c +++ b/tests/qtest/pca9554-test.c @@ -67,6 +67,41 @@ static void test_input_pullup(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); } +/* + * Polarity inversion: reading INPUT returns the XOR of the pin levels and the + * polarity register. + */ +static void test_polarity_inversion(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xFF); + + i2c_set8(dev, PCA9554_POLARITY, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); + + i2c_set8(dev, PCA9554_POLARITY, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xF0); +} + +/* Polarity inversion combined with output-driven pins. */ +static void test_polarity_with_output(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9554_CONFIG, 0x00); + i2c_set8(dev, PCA9554_OUTPUT, 0xA5); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0xA5); + + i2c_set8(dev, PCA9554_POLARITY, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x5A); + + /* Inversion only affects the INPUT read, not the OUTPUT register. */ + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0xA5); +} + static void pca9554_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -81,6 +116,10 @@ static void pca9554_register_nodes(void) qos_add_test("output-drives-input", "pca9554", test_output_drives_input, NULL); qos_add_test("input-pullup", "pca9554", test_input_pullup, NULL); + qos_add_test("polarity-inversion", "pca9554", test_polarity_inversion, + NULL); + qos_add_test("polarity-with-output", "pca9554", test_polarity_with_output, + NULL); } libqos_init(pca9554_register_nodes); From f9bf753b58c83d689f535f6b1ab217f4a46aa35f Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:14 +0200 Subject: [PATCH 360/860] tests/qtest: pca9554: test absence of command auto-increment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA9554 selects one of its four registers with a single command byte and does not auto-increment the register pointer, so a multi-byte I2C transfer keeps addressing the register chosen by the command byte instead of walking through the register map. Add a test covering this: a two-byte read returns the addressed register twice, and a two-byte write updates only that register, leaving its neighbour untouched. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-22-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9554-test.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/qtest/pca9554-test.c b/tests/qtest/pca9554-test.c index 74237df611..0da1f3304b 100644 --- a/tests/qtest/pca9554-test.c +++ b/tests/qtest/pca9554-test.c @@ -102,6 +102,33 @@ static void test_polarity_with_output(void *obj, void *data, g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0xA5); } +/* + * The PCA9554 has no auto-increment: the command pointer never advances, so + * multi-byte reads and writes all target the addressed register. + */ +static void test_no_autoincrement(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + uint8_t buf[2]; + + /* Distinct values in adjacent registers. */ + i2c_set8(dev, PCA9554_OUTPUT, 0xAA); + i2c_set8(dev, PCA9554_POLARITY, 0x33); + + /* Two reads from OUTPUT return OUTPUT twice, not OUTPUT then POLARITY. */ + i2c_read_block(dev, PCA9554_OUTPUT, buf, 2); + g_assert_cmphex(buf[0], ==, 0xAA); + g_assert_cmphex(buf[1], ==, 0xAA); + + /* The second written byte overwrites OUTPUT; POLARITY is untouched. */ + buf[0] = 0x12; + buf[1] = 0x34; + i2c_write_block(dev, PCA9554_OUTPUT, buf, 2); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x34); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x33); +} + static void pca9554_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -120,6 +147,7 @@ static void pca9554_register_nodes(void) NULL); qos_add_test("polarity-with-output", "pca9554", test_polarity_with_output, NULL); + qos_add_test("no-autoincrement", "pca9554", test_no_autoincrement, NULL); } libqos_init(pca9554_register_nodes); From b3fa8825086dcf217ea34dd1073cfd2fc0a790c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:15 +0200 Subject: [PATCH 361/860] tests/qtest: pca9554: test the PCA9536 4-bit variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCA9536 shares the PCA9554 register map and code path but exposes only four pins. Add a pca9536 node and check its reset defaults and output-to-input reflection are masked to the low nibble. Signed-off-by: Emmanuel Blot Reviewed-by: Glenn Miles Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-23-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- tests/qtest/pca9554-test.c | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/qtest/pca9554-test.c b/tests/qtest/pca9554-test.c index 0da1f3304b..5366e71984 100644 --- a/tests/qtest/pca9554-test.c +++ b/tests/qtest/pca9554-test.c @@ -129,6 +129,66 @@ static void test_no_autoincrement(void *obj, void *data, g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x33); } +/* + * The PCA9536 shares the PCA9554 register map but only has four pins, so its + * reset defaults and pin logic are masked to the low nibble. + */ +static void test_pca9536_reset_defaults(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x0F); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0x0F); +} + +/* Only the four low pins are driven; the upper nibble stays low. */ +static void test_pca9536_output_drives_input(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + i2c_set8(dev, PCA9554_CONFIG, 0x00); + + i2c_set8(dev, PCA9554_OUTPUT, 0x0A); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0A); + + i2c_set8(dev, PCA9554_OUTPUT, 0x00); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x00); +} + +/* + * The four upper bits address pins that do not exist on the PCA9536, so writes + * to the register map discard them: the writable registers read back with bits + * [7:4] cleared, and driving them onto the pins never surfaces in INPUT. + */ +static void test_pca9536_ignores_upper_bits(void *obj, void *data, + QGuestAllocator *alloc) +{ + QI2CDevice *dev = (QI2CDevice *)obj; + + /* Bits [7:4] are dropped on write; bits [3:0] survive. */ + i2c_set8(dev, PCA9554_OUTPUT, 0xFA); + g_assert_cmphex(i2c_get8(dev, PCA9554_OUTPUT), ==, 0x0A); + + i2c_set8(dev, PCA9554_POLARITY, 0xF5); + g_assert_cmphex(i2c_get8(dev, PCA9554_POLARITY), ==, 0x05); + + i2c_set8(dev, PCA9554_CONFIG, 0xF3); + g_assert_cmphex(i2c_get8(dev, PCA9554_CONFIG), ==, 0x03); + + /* + * With all four pins as outputs, driving 0xFF only affects the low + * nibble. + */ + i2c_set8(dev, PCA9554_POLARITY, 0x00); + i2c_set8(dev, PCA9554_CONFIG, 0x00); + i2c_set8(dev, PCA9554_OUTPUT, 0xFF); + g_assert_cmphex(i2c_get8(dev, PCA9554_INPUT), ==, 0x0F); +} + static void pca9554_register_nodes(void) { QOSGraphEdgeOptions opts = { @@ -148,6 +208,16 @@ static void pca9554_register_nodes(void) qos_add_test("polarity-with-output", "pca9554", test_polarity_with_output, NULL); qos_add_test("no-autoincrement", "pca9554", test_no_autoincrement, NULL); + + qos_node_create_driver("pca9536", i2c_device_create); + qos_node_consumes("pca9536", "i2c-bus", &opts); + + qos_add_test("reset-defaults", "pca9536", test_pca9536_reset_defaults, + NULL); + qos_add_test("output-drives-input", "pca9536", + test_pca9536_output_drives_input, NULL); + qos_add_test("ignores-upper-bits", "pca9536", + test_pca9536_ignores_upper_bits, NULL); } libqos_init(pca9554_register_nodes); From 49ec283f15868eae0094f94844db9c5a171c7592 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:16 +0200 Subject: [PATCH 362/860] hw/arm: catalina: model PCA9555 IO expanders with their own type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Catalina BMC device tree describes several IO expanders as nxp,pca9555. These were previously instantiated as PCA9552 devices as no PCA9555 model existed. Now that a dedicated PCA9555 device is available, use it so the emulated IO expanders match the hardware. Signed-off-by: Emmanuel Blot Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-24-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600_catalina.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c index 65495a524e..f3b22e9aa9 100644 --- a/hw/arm/aspeed_ast2600_catalina.c +++ b/hw/arm/aspeed_ast2600_catalina.c @@ -533,7 +533,7 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) TYPE_PCA9554, 0x27); /* io_expander6 - pca9555@25 */ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6), - TYPE_PCA9552, 0x25); + TYPE_PCA9555, 0x25); /* eeprom@51 */ at24c_eeprom_init_rom(pca954x_i2c_get_bus(i2c_mux, 6), 0x51, 8 * KiB, osfp_eeprom, osfp_eeprom_len); @@ -547,11 +547,11 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c2 */ /* io_expander0 - pca9555@20 */ - i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x20); + i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x20); /* io_expander0 - pca9555@21 */ - i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x21); + i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x21); /* io_expander0 - pca9555@27 */ - i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x27); + i2c_slave_create_simple(i2c[2], TYPE_PCA9555, 0x27); /* eeprom@50 */ at24c_eeprom_init(i2c[2], 0x50, 8 * KiB); /* eeprom@51 */ @@ -572,13 +572,13 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c6 */ /* io_expander3 - pca9555@21 */ - i2c_slave_create_simple(i2c[6], TYPE_PCA9552, 0x21); + i2c_slave_create_simple(i2c[6], TYPE_PCA9555, 0x21); /* rtc@6f - nct3018y */ i2c_slave_create_simple(i2c[6], TYPE_DS1338, 0x6f); /* &i2c9 */ /* io_expander4 - pca9555@4f */ - i2c_slave_create_simple(i2c[9], TYPE_PCA9552, 0x4f); + i2c_slave_create_simple(i2c[9], TYPE_PCA9555, 0x4f); /* temperature-sensor@4b - tpm75 */ i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4b); /* eeprom@50 */ @@ -615,17 +615,17 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c14 */ /* io_expander9 - pca9555@10 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x10); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x10); /* io_expander10 - pca9555@11 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x11); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x11); /* io_expander11 - pca9555@12 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x12); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x12); /* io_expander12 - pca9555@13 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x13); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x13); /* io_expander13 - pca9555@14 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x14); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x14); /* io_expander14 - pca9555@15 */ - i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x15); + i2c_slave_create_simple(i2c[14], TYPE_PCA9555, 0x15); /* &i2c15 */ /* temperature-sensor@1f - tmp421 */ From 508678167c0a6bfb325014d5bd06f946a3c0a7b4 Mon Sep 17 00:00:00 2001 From: Emmanuel Blot Date: Thu, 9 Jul 2026 17:23:17 +0200 Subject: [PATCH 363/860] hw/arm: catalina: add NIC and FIO temperature sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Model the temperature sensors described by the Catalina device tree that have existing QEMU device models but were not yet instantiated: the four IOB NIC TMP421 sensors behind the i2c0 PCA9546 muxes at 0x71 and 0x75, and the FIO remote TMP75 sensor at 0x4f on the i2c1 mux. Signed-off-by: Emmanuel Blot Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260709-catalina-upgrade-v1-25-82a63fead90c@free.fr Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast2600_catalina.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/hw/arm/aspeed_ast2600_catalina.c b/hw/arm/aspeed_ast2600_catalina.c index f3b22e9aa9..f714c9d1b3 100644 --- a/hw/arm/aspeed_ast2600_catalina.c +++ b/hw/arm/aspeed_ast2600_catalina.c @@ -472,7 +472,16 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* &i2c0 */ /* i2c-mux@71 (PCA9546) on i2c0 */ - i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x71); + i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x71); + + /* i2c0mux0ch0 */ + /* IOB0 NIC0 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), + TYPE_TMP421, 0x1f); + /* i2c0mux0ch2 */ + /* IOB0 NIC1 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 2), + TYPE_TMP421, 0x1f); /* i2c-mux@72 (PCA9546) on i2c0 */ i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x72); @@ -489,7 +498,16 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x73); /* i2c-mux@75 (PCA9546) on i2c0 */ - i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x75); + i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x75); + + /* i2c0mux3ch0 */ + /* IOB1 NIC0 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 0), + TYPE_TMP421, 0x1f); + /* i2c0mux3ch2 */ + /* IOB1 NIC1 temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 2), + TYPE_TMP421, 0x1f); /* i2c-mux@76 (PCA9546) on i2c0 */ i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x76); @@ -544,6 +562,8 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) fio_eeprom, fio_eeprom_len); /* temperature-sensor@4b - tmp75 */ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 7), TYPE_TMP75, 0x4b); + /* temperature-sensor@4f - tmp75 (FIO remote) */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 7), TYPE_TMP75, 0x4f); /* &i2c2 */ /* io_expander0 - pca9555@20 */ @@ -564,11 +584,7 @@ static void catalina_bmc_i2c_init(AspeedMachineState *bmc) /* eeprom@52 */ at24c_eeprom_init_rom(pca954x_i2c_get_bus(i2c_mux, 6), 0x52, 8 * KiB, hdd_eeprom, hdd_eeprom_len); - /* i2c5mux0ch7 */ - /* ina230@40 - no model */ - /* ina230@41 - no model */ - /* ina230@44 - no model */ - /* ina230@45 - no model */ + /* i2c5mux0ch7 - empty */ /* &i2c6 */ /* io_expander3 - pca9555@21 */ From a5f7d655854a4da8767b2a17be698fb6cd68d709 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:19:57 +0000 Subject: [PATCH 364/860] hw/i2c/aspeed_i2c: Support the AST2700 master buffer mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2700 I2C controller can move master DMA payloads through its internal SRAM pool rather than DRAM. The Linux driver calls this "buffer mode" and selects it by default. Buffer mode reuses the master DMA command bits (TX/RX_DMA_EN) and the DMA length registers, so the only difference from a DRAM transfer is where the data comes from and goes to: an offset into the pool programmed in I2CM_DMA_TX/RX_ADDR. The I2CC_VERSION_CTRL FUNC_CFG_DMA_EN bit selects between the two. Implement I2CC_VERSION_CTRL and, when FUNC_CFG_DMA_EN is clear, move the payload through the pool buffer instead of DRAM. I2CC_VERSION_CTRL resets to all ones, so guests that never program it keep targeting DRAM and behave as before. The register sits above the register window of the earlier SoCs, which are therefore unaffected. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260804081955.1563537-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/i2c/aspeed_i2c.c | 79 +++++++++++++++++++++++++++++++++++++ include/hw/i2c/aspeed_i2c.h | 2 + 2 files changed, 81 insertions(+) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 27afcaecee..68bdcd0e25 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -159,6 +159,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, case A_I2CS_INTR_CTRL: case A_I2CS_DMA_LEN_STS: case A_I2CS_INTR_STS: + case A_I2CC_VERSION_CTRL: value = bus->regs[offset / sizeof(*bus->regs)]; break; case A_I2CC_DMA_ADDR: @@ -295,6 +296,65 @@ static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data) return 0; } +/* + * In AST2700 buffer mode the master DMA command bits (TX/RX_DMA_EN) and the + * DMA length registers are reused, but data is moved through the controller + * internal SRAM pool at the offset programmed in I2CM_DMA_TX/RX_ADDR instead + * of DRAM. FUNC_CFG_DMA_EN selects between the two (set = DRAM). + */ +static bool aspeed_i2c_bus_dma_to_pool(AspeedI2CBus *bus) +{ + return aspeed_i2c_is_new_mode(bus->controller) && + !ARRAY_FIELD_EX32(bus->regs, I2CC_VERSION_CTRL, FUNC_CFG_DMA_EN); +} + +static int aspeed_i2c_bus_send_dma_pool(AspeedI2CBus *bus) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t offset = bus->regs[R_I2CM_DMA_TX_ADDR]; + uint8_t *pool_base = aic->bus_pool_base(bus); + int ret = -1; + int i; + + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); + for (i = 0; bus->regs[reg_dma_len] && + offset + i < ASPEED_I2C_BUS_POOL_SIZE; i++) { + trace_aspeed_i2c_bus_send("BUFF", i + 1, bus->regs[reg_dma_len], + pool_base[offset + i]); + ret = i2c_send(bus->bus, pool_base[offset + i]); + bus->regs[reg_dma_len]--; + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, i + 1); + if (ret) { + break; + } + } + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0); + return ret; +} + +static void aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) +{ + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus); + uint32_t offset = bus->regs[R_I2CM_DMA_RX_ADDR]; + uint8_t *pool_base = aic->bus_pool_base(bus); + int i; + + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0); + for (i = 0; bus->regs[reg_dma_len] && + offset + i < ASPEED_I2C_BUS_POOL_SIZE; i++) { + pool_base[offset + i] = i2c_recv(bus->bus); + trace_aspeed_i2c_bus_recv("BUFF", i + 1, bus->regs[reg_dma_len], + pool_base[offset + i]); + bus->regs[reg_dma_len]--; + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, i + 1); + } + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0); +} + static int aspeed_i2c_bus_send(AspeedI2CBus *bus) { AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); @@ -320,6 +380,10 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus) } SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_BUFF_EN, 0); } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) { + /* In buffer mode the DMA moves data through the pool, not DRAM */ + if (aspeed_i2c_bus_dma_to_pool(bus)) { + return aspeed_i2c_bus_send_dma_pool(bus); + } /* In new mode, clear how many bytes we TXed */ if (aspeed_i2c_is_new_mode(bus->controller)) { ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0); @@ -385,6 +449,11 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) SHARED_ARRAY_FIELD_DP32(bus->regs, reg_pool_ctrl, RX_COUNT, i & 0xff); SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_BUFF_EN, 0); } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) { + /* In buffer mode the DMA moves data through the pool, not DRAM */ + if (aspeed_i2c_bus_dma_to_pool(bus)) { + aspeed_i2c_bus_recv_dma_pool(bus); + return; + } /* In new mode, clear how many bytes we RXed */ if (aspeed_i2c_is_new_mode(bus->controller)) { ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0); @@ -854,6 +923,9 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset, I2CS_DMA_RX_ADDR_HI, ADDR_HI); break; + case A_I2CC_VERSION_CTRL: + bus->regs[R_I2CC_VERSION_CTRL] = value; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -1497,6 +1569,13 @@ static void aspeed_i2c_bus_reset_hold(Object *obj, ResetType type) memset(s->regs, 0, sizeof(s->regs)); s->pending_intr_sts = 0; i2c_end_transfer(s->bus); + /* + * I2CC_VERSION_CTRL resets to all-ones. FUNC_CFG_DMA_EN is therefore set, + * so master DMA targets DRAM unless the guest clears it to select buffer + * mode. Guests unaware of buffer mode never touch this register and keep + * doing DRAM DMA. + */ + s->regs[R_I2CC_VERSION_CTRL] = 0xffffffff; } static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp) diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 156998e7c1..05937a7a0b 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -231,6 +231,8 @@ REG32(I2CS_DMA_TX_ADDR_HI, 0x68) FIELD(I2CS_DMA_TX_ADDR_HI, ADDR_HI, 0, 7) REG32(I2CS_DMA_RX_ADDR_HI, 0x6c) FIELD(I2CS_DMA_RX_ADDR_HI, ADDR_HI, 0, 7) +REG32(I2CC_VERSION_CTRL, 0x94) + FIELD(I2CC_VERSION_CTRL, FUNC_CFG_DMA_EN, 2, 1) struct AspeedI2CState; From 809720039fb4d04c11eb3ee7fe87b9e0f1a5f5fc Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:19:59 +0000 Subject: [PATCH 365/860] tests/functional/aarch64/test_aspeed_ast2700a2: Update ASPEED SDK v11.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/20260804081955.1563537-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- .../aarch64/test_aspeed_ast2700a2.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700a2.py b/tests/functional/aarch64/test_aspeed_ast2700a2.py index 5f9073c897..01c7c4fdbe 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a2.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a2.py @@ -85,13 +85,13 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - ASSET_SDK_V1101_AST2700A2 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-default-image.tar.gz', - 'ce89dcd995cf284d41a6a4bd17a1b97d59939f0277bfe54fdaaf30e741ce7487') + ASSET_SDK_V1103_AST2700A2 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-default-image.tar.gz', + 'b91450d53da234591060cfb926fa30f7534ce20eaab766cb0f80ec332f8f0adb') - ASSET_SDK_V1101_AST2700A2_DCSCM = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-dcscm-image.tar.gz', - 'b92ece9ca733dfd7a20193a12582f743b77f1898116b6d6f1abe57ac8db01c56') + ASSET_SDK_V1103_AST2700A2_DCSCM = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-dcscm-image.tar.gz', + '7afd8323fc95097c14872b90d68c5cae5d078530c704591b192b74bd892c1dbf') def do_ast2700_i2c_test(self, bus_id): bus_str = str(bus_id) @@ -135,7 +135,8 @@ class AST2x00MachineSDK(QemuSystemTest): }, { 'addr': '0x430000000', - 'file': self.scratch_file(name, 'bl31.bin') + 'file': self.scratch_file(name, 'trusted-firmware-a', + 'bl31.bin') }, { 'addr': '0x430080000', @@ -162,11 +163,11 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc'), bus_id) - def test_aarch64_ast2700a2_evb_sdk_v11_01(self): + def test_aarch64_ast2700a2_evb_sdk_v11_03(self): self.set_machine('ast2700a2-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A2) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A2) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') self.vm.add_args('-device', 'usb-kbd,bus=usb-bus.3') @@ -176,22 +177,22 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_ast2700_pcie_test() self.do_ast2700_usb_ehci_test() - def test_aarch64_ast2700a2_evb_sdk_vbootrom_v11_01(self): + def test_aarch64_ast2700a2_evb_sdk_vbootrom_v11_03(self): self.set_machine('ast2700a2-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A2) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A2) 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-image', 1) self.verify_vbootrom_firmware_flow() self.verify_openbmc_boot_start() - def test_aarch64_ast2700a2_evb_ioexp_v11_01(self): + def test_aarch64_ast2700a2_evb_ioexp_v11_03(self): self.set_machine('ast2700a2-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A2_DCSCM) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A2_DCSCM) self.vm.set_machine('ast2700a2-evb,fmc-model=w25q512jv') self.vm.add_args('-device', 'tmp105,bus=ioexp0.0,address=0x4d,id=tmp-test-16') From 169d499d6554f6261776366f2e77444a14265764 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:20:00 +0000 Subject: [PATCH 366/860] tests/functional/aarch64/test_aspeed_ast2700a1: Update ASPEED SDK v11.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/20260804081955.1563537-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- .../aarch64/test_aspeed_ast2700a1.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700a1.py b/tests/functional/aarch64/test_aspeed_ast2700a1.py index adbbef5cf1..2113c78db2 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700a1.py +++ b/tests/functional/aarch64/test_aspeed_ast2700a1.py @@ -85,13 +85,13 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - ASSET_SDK_V1101_AST2700A1 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-a1-image.tar.gz', - '859808828531a51931aad3b4e70b28143eebb3cde1838ba7d8e7a2b844c8a1ab') + ASSET_SDK_V1103_AST2700A1 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-a1-image.tar.gz', + '540961dc380709d852e957c5817cd7ee0dbb0a66f3aa17413eac7b67518afbfe') - ASSET_SDK_V1101_AST2700A1_DCSCM = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-a1-dcscm-image.tar.gz', - '4654eabad75da3fd33635cd6d29b7635181daefee7294b68feb124b9d4c24116') + ASSET_SDK_V1103_AST2700A1_DCSCM = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-a1-dcscm-image.tar.gz', + '5f7c139330fcefa6025bc7565a20fd5ea42cacebf810d56dc9a76b07cf69b3e1') def do_ast2700_i2c_test(self, bus_id): bus_str = str(bus_id) @@ -132,7 +132,8 @@ class AST2x00MachineSDK(QemuSystemTest): }, { 'addr': '0x430000000', - 'file': self.scratch_file(name, 'bl31.bin') + 'file': self.scratch_file(name, 'trusted-firmware-a', + 'bl31.bin') }, { 'addr': '0x430080000', @@ -159,11 +160,11 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc'), bus_id) - def test_aarch64_ast2700a1_evb_sdk_v11_01(self): + def test_aarch64_ast2700a1_evb_sdk_v11_03(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A1) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A1) self.vm.add_args('-device', 'e1000e,netdev=net1,bus=pcie.2') self.vm.add_args('-netdev', 'user,id=net1') self.vm.add_args('-device', 'usb-kbd,bus=usb-bus.3') @@ -173,22 +174,22 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_ast2700_pcie_test() self.do_ast2700_usb_ehci_test() - def test_aarch64_ast2700a1_evb_sdk_vbootrom_v11_01(self): + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v11_03(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A1) + self.archive_extract(self.ASSET_SDK_V1103_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-a1-image', 1) self.verify_vbootrom_firmware_flow() self.verify_openbmc_boot_start() - def test_aarch64_ast2700a1_evb_ioexp_v11_01(self): + def test_aarch64_ast2700a1_evb_ioexp_v11_03(self): self.set_machine('ast2700a1-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700A1_DCSCM) + self.archive_extract(self.ASSET_SDK_V1103_AST2700A1_DCSCM) self.vm.set_machine('ast2700a1-evb,fmc-model=w25q512jv') self.vm.add_args('-device', 'tmp105,bus=ioexp0.0,address=0x4d,id=tmp-test-16') From ea2387e2115092dc1acfb69c8f979c2eb0849801 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:20:01 +0000 Subject: [PATCH 367/860] tests/functional/aarch64/test_aspeed_ast2700fc: Update ASPEED SDK v11.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/20260804081955.1563537-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- .../aarch64/test_aspeed_ast2700fc.py | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py index 86270e6111..704477d7c5 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700fc.py +++ b/tests/functional/aarch64/test_aspeed_ast2700fc.py @@ -66,9 +66,9 @@ class AST2x00MachineSDK(QemuSystemTest): self.vm.add_args('-device', f'loader,file={file},cpu-num={cpu_num}') - ASSET_SDK_V1101_AST2700 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2700-default-image.tar.gz', - 'ce89dcd995cf284d41a6a4bd17a1b97d59939f0277bfe54fdaaf30e741ce7487') + ASSET_SDK_V1103_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2700-default-image.tar.gz', + 'b91450d53da234591060cfb926fa30f7534ce20eaab766cb0f80ec332f8f0adb') def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, @@ -101,7 +101,7 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, '\012', 'ssp_tsp:~$') exec_command_and_wait_for_pattern(self, 'version', - 'Zephyr version 3.7.1') + 'Zephyr version 3.7.2') exec_command_and_wait_for_pattern(self, 'md 72c02000 1', '[72c02000] 06020103') @@ -112,7 +112,7 @@ class AST2x00MachineSDK(QemuSystemTest): exec_command_and_wait_for_pattern(self, '\012', 'tsp:~$') exec_command_and_wait_for_pattern(self, 'version', - 'Zephyr version 3.7.1') + 'Zephyr version 3.7.2') exec_command_and_wait_for_pattern(self, 'md 72c02000 1', '[72c02000] 06020103') @@ -125,7 +125,8 @@ class AST2x00MachineSDK(QemuSystemTest): }, { 'addr': '0x430000000', - 'file': self.scratch_file(name, 'bl31.bin') + 'file': self.scratch_file(name, 'trusted-firmware-a', + 'bl31.bin') }, { 'addr': '0x430080000', @@ -153,11 +154,11 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) - def test_aarch64_ast2700fc_sdk_v11_01(self): + def test_aarch64_ast2700fc_sdk_v11_03(self): self.set_machine('ast2700fc') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2700) + self.archive_extract(self.ASSET_SDK_V1103_AST2700) self.start_ast2700fc_test('ast2700-default-image') self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() @@ -165,10 +166,10 @@ class AST2x00MachineSDK(QemuSystemTest): self.do_ast2700fc_ssp_test() self.do_ast2700fc_tsp_test() - def test_aarch64_ast2700fc_sdk_vbootrom_v11_01(self): + def test_aarch64_ast2700fc_sdk_vbootrom_v11_03(self): self.set_machine('ast2700fc') - self.archive_extract(self.ASSET_SDK_V1101_AST2700) + self.archive_extract(self.ASSET_SDK_V1103_AST2700) self.start_ast2700fc_test_vbootrom('ast2700-default-image') self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700fc_ssp_test() From 769661a1ac97e8abc11c041eba2de6d317543cc1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:20:02 +0000 Subject: [PATCH 368/860] tests/functional/arm/test_aspeed_ast2600_sdk: Update ASPEED SDK v11.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/20260804081955.1563537-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast2600_sdk.py | 8 ++++---- tests/functional/arm/test_aspeed_ast2600_sdk_515.py | 8 ++++---- tests/functional/arm/test_aspeed_ast2600_sdk_otp.py | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk.py b/tests/functional/arm/test_aspeed_ast2600_sdk.py index cabbe230c4..4fc594dfd5 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk.py @@ -14,9 +14,9 @@ from qemu_test import exec_command_and_wait_for_pattern class AST2600Machine(AspeedTest): - ASSET_SDK_V1101_AST2600 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2600-default-image.tar.gz', - '3c5b4d4ccf27b0d208a073f98426db54cd751b96143180cd15df1a83978f832c') + ASSET_SDK_V1103_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2600-default-image.tar.gz', + '47e3656a14bf7a4de28d3dfbf48bc2325443bc42d270f3bc82646f92f6dea165') def do_ast2600_pcie_test(self): exec_command_and_wait_for_pattern(self, @@ -49,7 +49,7 @@ class AST2600Machine(AspeedTest): self.set_machine('ast2600-evb') self.require_netdev('user') - self.archive_extract(self.ASSET_SDK_V1101_AST2600) + self.archive_extract(self.ASSET_SDK_V1103_AST2600) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk_515.py b/tests/functional/arm/test_aspeed_ast2600_sdk_515.py index a8e7faff48..f5b14de083 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk_515.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk_515.py @@ -10,14 +10,14 @@ from aspeed import AspeedTest class AST2600Machine(AspeedTest): - ASSET_SDK_V1101_AST2600_515 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2600-default-515-image.tar.gz', - 'f3ccf1c08db71cf891637fc73131b80b2c0c0e005c06d5dcae0cf74fc458b43c') + ASSET_SDK_V1103_AST2600_515 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2600-default-515-image.tar.gz', + 'c79d0197106f146476e82bb878e5438f6569bd30f3b53fbb520b59bc54f6b7dc') def test_arm_ast2600_evb_sdk_515(self): self.set_machine('ast2600-evb') - self.archive_extract(self.ASSET_SDK_V1101_AST2600_515) + self.archive_extract(self.ASSET_SDK_V1103_AST2600_515) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2600-default-515-image", "image-bmc")) diff --git a/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py b/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py index f24dea1e8f..5813c59bd6 100755 --- a/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py +++ b/tests/functional/arm/test_aspeed_ast2600_sdk_otp.py @@ -12,15 +12,15 @@ from qemu_test import exec_command_and_wait_for_pattern class AST2600Machine(AspeedTest): - ASSET_SDK_V1101_AST2600 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2600-default-image.tar.gz', - '3c5b4d4ccf27b0d208a073f98426db54cd751b96143180cd15df1a83978f832c') + ASSET_SDK_V1103_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2600-default-image.tar.gz', + '47e3656a14bf7a4de28d3dfbf48bc2325443bc42d270f3bc82646f92f6dea165') def test_arm_ast2600_otp_blockdev_device(self): self.vm.set_machine("ast2600-evb") self.require_netdev('user') - image_path = self.archive_extract(self.ASSET_SDK_V1101_AST2600) + image_path = self.archive_extract(self.ASSET_SDK_V1103_AST2600) otp_img = self.generate_otpmem_image() self.vm.set_console() From 41bb72aed926a149a24c09e0f2b6228eee186a56 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:20:03 +0000 Subject: [PATCH 369/860] tests/functional/arm/test_aspeed_ast2500_sdk: Update ASPEED SDK v11.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/20260804081955.1563537-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast2500_sdk.py | 8 ++++---- tests/functional/arm/test_aspeed_ast2500_sdk_515.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2500_sdk.py b/tests/functional/arm/test_aspeed_ast2500_sdk.py index 6ab498b3ff..95df32b84f 100755 --- a/tests/functional/arm/test_aspeed_ast2500_sdk.py +++ b/tests/functional/arm/test_aspeed_ast2500_sdk.py @@ -10,14 +10,14 @@ from aspeed import AspeedTest class AST2500Machine(AspeedTest): - ASSET_SDK_V1101_AST2500 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2500-default-obmc.tar.gz', - '3faa1188198da2216837be4b53861c483a58c3ad63784089720bf8421e157da1') + ASSET_SDK_V1103_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2500-default-obmc.tar.gz', + '8e20cafddca04d73b799918d6f35b08c83c9f024e223a317b0ad71b97b84842f') def test_arm_ast2500_evb_sdk(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V1101_AST2500) + self.archive_extract(self.ASSET_SDK_V1103_AST2500) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) diff --git a/tests/functional/arm/test_aspeed_ast2500_sdk_515.py b/tests/functional/arm/test_aspeed_ast2500_sdk_515.py index 8d39dc65da..516e96e52d 100755 --- a/tests/functional/arm/test_aspeed_ast2500_sdk_515.py +++ b/tests/functional/arm/test_aspeed_ast2500_sdk_515.py @@ -10,14 +10,14 @@ from aspeed import AspeedTest class AST2500Machine(AspeedTest): - ASSET_SDK_V1101_AST2500_515 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.01/ast2500-default-515-obmc.tar.gz', - 'b848ff620d2e9c83e2fb4736b4d1c39b82fdb041058cd42be42c3b177bf38eb9') + ASSET_SDK_V1103_AST2500_515 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v11.03/ast2500-default-515-obmc.tar.gz', + 'f17d3b0a5157bcf73c21c4981f838ea0b76c6406cc4a6409267d57d61758ebb6') def test_arm_ast2500_evb_sdk_515(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V1101_AST2500_515) + self.archive_extract(self.ASSET_SDK_V1103_AST2500_515) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default-515", "image-bmc")) From 274a9cfdeb6edaf3aec4aacae9f7187230042f00 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:20:05 +0000 Subject: [PATCH 370/860] tests/functional/arm/test_aspeed_ast1030: Update ASPEED Zephyr SDK v03.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/20260804081955.1563537-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- 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 03fee55b5f..83a96ec322 100755 --- a/tests/functional/arm/test_aspeed_ast1030.py +++ b/tests/functional/arm/test_aspeed_ast1030.py @@ -12,17 +12,17 @@ from qemu_test import Asset, exec_command_and_wait_for_pattern class AST1030Machine(AspeedTest): - ASSET_ZEPHYR_3_06 = Asset( + ASSET_ZEPHYR_3_08 = Asset( ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.03.06/ast1030-evb-demo.zip'), - '056f37fcd9f165308cedca3a08f2bed37ed40c0a1402c4fa515613b80a369f38') + '/zephyr/releases/download/v00.03.08/ast1030-evb-demo.zip'), + '9eac3691bc7bce1b912bbe2ae4e36608a6532ff8d607f4d1e44b88407a48d4e5') - def test_arm_ast1030_zephyros_3_06(self): + def test_arm_ast1030_zephyros_3_08(self): self.set_machine('ast1030-evb') kernel_name = "ast1030-evb-demo/zephyr.elf" kernel_file = self.archive_extract( - self.ASSET_ZEPHYR_3_06, member=kernel_name) + self.ASSET_ZEPHYR_3_08, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') @@ -72,7 +72,7 @@ class AST1030Machine(AspeedTest): self.vm.set_machine("ast1030-evb") kernel_name = "ast1030-evb-demo/zephyr.elf" - kernel_file = self.archive_extract(self.ASSET_ZEPHYR_3_06, + kernel_file = self.archive_extract(self.ASSET_ZEPHYR_3_08, member=kernel_name) otp_img = self.generate_otpmem_image() From 007ae802b45dca42616bb2be3d076d0753f99179 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 4 Aug 2026 08:20:06 +0000 Subject: [PATCH 371/860] tests/functional/arm/test_aspeed_ast1060: Update ASPEED ZEPHYR PROJECT v03.07 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/20260804081955.1563537-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/arm/test_aspeed_ast1060.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast1060.py b/tests/functional/arm/test_aspeed_ast1060.py index 833cfb8272..d7158259e6 100755 --- a/tests/functional/arm/test_aspeed_ast1060.py +++ b/tests/functional/arm/test_aspeed_ast1060.py @@ -11,18 +11,18 @@ from qemu_test import Asset, exec_command_and_wait_for_pattern class AST1060Machine(AspeedTest): - ASSET_ASPEED_AST1060_PROT_3_05 = Asset( + ASSET_ASPEED_AST1060_PROT_3_07 = Asset( ('https://github.com/AspeedTech-BMC' - '/aspeed-zephyr-project/releases/download/v03.05' - '/ast1060_prot_v03.05.tgz'), - '63b36d7420290726ca80477de254474b7cb79539a42819bb1fe2665d598dadb5') + '/aspeed-zephyr-project/releases/download/v03.07' + '/ast1060_prot_v03.07.tgz'), + '55a7f51f0b77051a0ef2ada993a16c5033768e1b8e8be3babfc52c303eecd07f') - def test_arm_ast1060_prot_3_05(self): + def test_arm_ast1060_prot_3_07(self): self.set_machine('ast1060-evb') kernel_name = "ast1060_prot/zephyr.bin" kernel_file = self.archive_extract( - self.ASSET_ASPEED_AST1060_PROT_3_05, member=kernel_name) + self.ASSET_ASPEED_AST1060_PROT_3_07, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') @@ -35,7 +35,7 @@ class AST1060Machine(AspeedTest): self.vm.set_machine("ast1060-evb") kernel_name = "ast1060_prot/zephyr.bin" - kernel_file = self.archive_extract(self.ASSET_ASPEED_AST1060_PROT_3_05, + kernel_file = self.archive_extract(self.ASSET_ASPEED_AST1060_PROT_3_07, member=kernel_name) otp_img = self.generate_otpmem_image() From 1d06e84aa75e880ba1e859c6ee157306702ab3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlo=20Marcelo=20Arenas=20Bel=C3=B3n?= Date: Sun, 2 Aug 2026 09:28:28 -0700 Subject: [PATCH 372/860] hw/arm/aspeed: avoid sign mismatch on sscanf for uart property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit using "%u" with sscanf() was likely meant to indicate that a negative value was unexpected, but with a signed variable it could result in undefined behaviour. use "%d" and check for a negative input explicitly. Signed-off-by: Carlo Marcelo Arenas Belón Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260802162828.16880-1-carenas@gmail.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a48c442058..a9238e6217 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -327,7 +327,7 @@ static void aspeed_set_bmc_console(Object *obj, const char *value, Error **errp) int uart_first = aspeed_uart_first(sc->uarts_base); int uart_last = aspeed_uart_last(sc->uarts_base, sc->uarts_num); - if (sscanf(value, "uart%u", &val) != 1) { + if (sscanf(value, "uart%d", &val) != 1 || val < 0) { error_setg(errp, "Bad value for \"uart\" property"); return; } From c264c5175ee0672169923eb61d3bd3dd7018db1a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:18 +0000 Subject: [PATCH 373/860] hw/misc/aspeed_hace: Support the crypto command in direct access mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The crypt command register was previously stubbed out. Implement it for the direct access mode, where HACE00/HACE04 point directly at contiguous source and destination buffers. AES-128/192/256, DES and 3DES are supported in ECB and CBC modes via the qcrypto cipher API; the IV and key are read from the context buffer (HACE08) and, for CBC, the resulting chaining IV is written back to the context. The completion interrupt is now raised for every HACE variant as the hardware does, which fixes the crypt command hang on the AST2500, AST2600 and AST1030. The AST2700 crypto engine still needs 64-bit DMA and AES-GCM, which are added later, so it keeps its temporary interrupt-only workaround until then. For debugging, the context, source and destination buffers are dumped through the existing aspeed_hace_hexdump trace event (disabled by default). CTR mode, scatter-gather mode and AES-GCM are added separately. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 283 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 268 insertions(+), 15 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index c61efe50c4..0f35d74137 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -18,11 +18,43 @@ #include "qapi/error.h" #include "migration/vmstate.h" #include "crypto/hash.h" +#include "crypto/cipher.h" #include "hw/core/qdev-properties.h" #include "hw/core/irq.h" #include "trace.h" -#define R_CRYPT_CMD (0x10 / 4) +/* Crypto engine registers */ +#define R_CRYPT_SRC (0x00 / 4) +#define R_CRYPT_DEST (0x04 / 4) +#define R_CRYPT_CONTEXT (0x08 / 4) +#define R_CRYPT_DATA_LEN (0x0c / 4) +/* HACE0C[27:0] holds the crypto data length */ +#define CRYPT_DATA_LEN_MASK 0x0FFFFFFF +#define R_CRYPT_CMD (0x10 / 4) +/* Crypto engine command register (HACE10) bits */ +#define CRYPT_CMD_ENCRYPT BIT(7) +#define CRYPT_CMD_ISR_EN BIT(12) +#define CRYPT_CMD_DES_SELECT BIT(16) +#define CRYPT_CMD_TRIPLE_DES BIT(17) +#define CRYPT_CMD_SRC_SG_CTRL BIT(18) +/* Operation mode HACE10[6:4] */ +#define CRYPT_CMD_OP_MODE_MASK (0x7 << 4) +#define CRYPT_CMD_ECB (0x0 << 4) +#define CRYPT_CMD_CBC (0x1 << 4) +/* AES key length HACE10[3:2] */ +#define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2) +#define CRYPT_CMD_AES256 (0x2 << 2) +#define CRYPT_CMD_AES192 (0x1 << 2) +#define CRYPT_CMD_AES128 (0x0 << 2) + +/* + * Crypto context buffer layout (HACE08). The IV is at the start of the buffer + * (DES places its 8 byte IV at offset 8) and the cipher key at offset 0x10. + */ +#define CRYPT_CTX_IV_OFFSET 0x00 +#define CRYPT_CTX_DES_IV_OFFSET 0x08 +#define CRYPT_CTX_KEY_OFFSET 0x10 +#define CRYPT_CTX_SIZE 0x30 #define R_STATUS (0x1c / 4) #define HASH_IRQ BIT(9) @@ -65,7 +97,6 @@ /* Other cmd bits */ #define HASH_IRQ_EN BIT(9) #define HASH_SG_EN BIT(18) -#define CRYPT_IRQ_EN BIT(12) /* Scatter-gather data list */ #define SG_LIST_LEN_SIZE 4 #define SG_LIST_LEN_MASK 0x0FFFFFFF @@ -501,6 +532,216 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, } } +static bool crypt_aes_alg(uint32_t cmd, QCryptoCipherAlgo *alg, size_t *keylen) +{ + switch (cmd & CRYPT_CMD_AES_KEY_LEN_MASK) { + case CRYPT_CMD_AES128: + *alg = QCRYPTO_CIPHER_ALGO_AES_128; + *keylen = 16; + break; + case CRYPT_CMD_AES192: + *alg = QCRYPTO_CIPHER_ALGO_AES_192; + *keylen = 24; + break; + case CRYPT_CMD_AES256: + *alg = QCRYPTO_CIPHER_ALGO_AES_256; + *keylen = 32; + break; + default: + return false; + } + + return true; +} + +/* + * Decode the crypto command register into a libqcrypto algorithm/mode pair + * and the block/IV geometry. Returns false for unsupported selections. + */ +static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg, + QCryptoCipherMode *mode, size_t *keylen, + size_t *blocklen, size_t *iv_offset) +{ + if (cmd & CRYPT_CMD_DES_SELECT) { + *blocklen = 8; + *iv_offset = CRYPT_CTX_DES_IV_OFFSET; + if (cmd & CRYPT_CMD_TRIPLE_DES) { + *alg = QCRYPTO_CIPHER_ALGO_3DES; + *keylen = 24; + } else { + *alg = QCRYPTO_CIPHER_ALGO_DES; + *keylen = 8; + } + } else { + *blocklen = 16; + *iv_offset = CRYPT_CTX_IV_OFFSET; + if (!crypt_aes_alg(cmd, alg, keylen)) { + return false; + } + } + + switch (cmd & CRYPT_CMD_OP_MODE_MASK) { + case CRYPT_CMD_ECB: + *mode = QCRYPTO_CIPHER_MODE_ECB; + break; + case CRYPT_CMD_CBC: + *mode = QCRYPTO_CIPHER_MODE_CBC; + break; + default: + return false; + } + + return true; +} + +/* + * Direct access mode: the source/destination register (HACE00/HACE04) points + * at a single contiguous buffer in DRAM. Copy @len bytes between it and the + * bounce buffer @buf; when @to_dram is true @buf is written out, otherwise it + * is read in. Returns true on success. + */ +static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr, + uint8_t *buf, uint32_t len, bool to_dram) +{ + return !address_space_rw(&s->dram_as, addr, MEMTXATTRS_UNSPECIFIED, + buf, len, to_dram); +} + +/* + * Perform an AES/DES/3DES ECB/CBC operation in direct access mode: the source + * and destination are single contiguous buffers (HACE00/HACE04) and the IV/key + * come from the context buffer (HACE08). For CBC the resulting chaining IV is + * written back to the context buffer so the driver can continue the chain. + */ +static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) +{ + uint32_t len = s->regs[R_CRYPT_DATA_LEN]; + bool encrypt = cmd & CRYPT_CMD_ENCRYPT; + g_autoptr(QCryptoCipher) cipher = NULL; + g_autofree uint8_t *src_buf = NULL; + g_autofree uint8_t *dst_buf = NULL; + uint8_t ctx[CRYPT_CTX_SIZE]; + Error *local_err = NULL; + QCryptoCipherMode mode; + QCryptoCipherAlgo alg; + const uint8_t *next_iv; + uint64_t ctx_addr; + uint64_t src_addr; + uint64_t dst_addr; + size_t iv_offset; + size_t blocklen; + size_t keylen; + + if (len == 0) { + return; + } + + if (!crypt_decode_cmd(cmd, &alg, &mode, &keylen, &blocklen, &iv_offset)) { + qemu_log_mask(LOG_UNIMP, + "%s: Unsupported crypt command 0x%x\n", __func__, cmd); + return; + } + + if (!qcrypto_cipher_supports(alg, mode)) { + qemu_log_mask(LOG_UNIMP, + "%s: cipher mode not supported by the crypto backend\n", + __func__); + return; + } + + /* Fetch the IV and key from the context buffer in DRAM. */ + ctx_addr = s->regs[R_CRYPT_CONTEXT]; + if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED, + ctx, sizeof(ctx))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read context, addr=0x%" HWADDR_PRIx "\n", + __func__, ctx_addr); + return; + } + + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("context", (char *)ctx, sizeof(ctx)); + } + + cipher = qcrypto_cipher_new(alg, mode, ctx + CRYPT_CTX_KEY_OFFSET, keylen, + &local_err); + if (cipher == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher new failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + + if (mode != QCRYPTO_CIPHER_MODE_ECB && + qcrypto_cipher_setiv(cipher, ctx + iv_offset, blocklen, + &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + + src_buf = g_malloc0(len); + dst_buf = g_malloc0(len); + + src_addr = s->regs[R_CRYPT_SRC]; + if (!crypt_prepare_direct(s, src_addr, src_buf, len, false)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to read src, addr=0x%" HWADDR_PRIx "\n", + __func__, src_addr); + return; + } + + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("src", (char *)src_buf, len); + } + + if (encrypt) { + if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len, + &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + } else { + if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len, + &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + } + + dst_addr = s->regs[R_CRYPT_DEST]; + if (!crypt_prepare_direct(s, dst_addr, dst_buf, len, true)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write dst, addr=0x%" HWADDR_PRIx "\n", + __func__, dst_addr); + return; + } + + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("dst", (char *)dst_buf, len); + } + + if (mode == QCRYPTO_CIPHER_MODE_CBC) { + /* + * CBC chains on the last ciphertext block: the final block of the + * output when encrypting, or of the input when decrypting. Write it + * back as the IV for the next request. + */ + next_iv = (encrypt ? dst_buf : src_buf) + len - blocklen; + if (address_space_write(&s->dram_as, ctx_addr + iv_offset, + MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n", + __func__, ctx_addr + iv_offset); + } + } +} + static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) { AspeedHACEState *s = ASPEED_HACE(opaque); @@ -531,16 +772,22 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, qemu_irq_lower(s->irq); } } - if (ahc->raise_crypt_interrupt_workaround) { - if (data & CRYPT_IRQ) { - data &= ~CRYPT_IRQ; + if (data & CRYPT_IRQ) { + data &= ~CRYPT_IRQ; - if (s->regs[addr] & CRYPT_IRQ) { - qemu_irq_lower(s->irq); - } + if (s->regs[addr] & CRYPT_IRQ) { + qemu_irq_lower(s->irq); } } break; + case R_CRYPT_SRC: + case R_CRYPT_DEST: + case R_CRYPT_CONTEXT: + data &= ahc->src_mask; + break; + case R_CRYPT_DATA_LEN: + data &= CRYPT_DATA_LEN_MASK; + break; case R_HASH_SRC: data &= ahc->src_mask; break; @@ -589,13 +836,19 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, break; } case R_CRYPT_CMD: - qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n", - __func__); - if (ahc->raise_crypt_interrupt_workaround) { - s->regs[R_STATUS] |= CRYPT_IRQ; - if (data & CRYPT_IRQ_EN) { - qemu_irq_raise(s->irq); - } + /* + * The AST2700 crypto engine needs 64-bit DMA and AES-GCM, which are + * added later; until then it keeps the temporary workaround of only + * raising the completion interrupt without running the command. + */ + if (!ahc->raise_crypt_interrupt_workaround) { + do_crypt_operation(s, data); + } + + /* Hardware raises the crypt interrupt once the command finishes. */ + s->regs[R_STATUS] |= CRYPT_IRQ; + if (data & CRYPT_CMD_ISR_EN) { + qemu_irq_raise(s->irq); } break; case R_HASH_SRC_HI: From d566fb6b1e1fb68007e72c221a54fcc1d7bc056c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:19 +0000 Subject: [PATCH 374/860] tests/qtest/aspeed-hace: Test the crypto command on the AST2500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a crypto known-answer test harness and exercise the AST2500, which uses the crypto engine's direct access mode. Each mode (AES/DES/3DES in ECB and CBC) is a separate test that checks the ciphertext, the plaintext round-trip and, for CBC, the chaining IV written back to the context buffer. The key/IV/plaintext/ciphertext values are taken verbatim from the Linux kernel crypto self-test templates in crypto/testmgr.h. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 325 ++++++++++++++++++++++++++++++++ tests/qtest/aspeed-hace-utils.h | 16 ++ tests/qtest/aspeed_hace-test.c | 6 + tests/qtest/meson.build | 6 +- 4 files changed, 351 insertions(+), 2 deletions(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 25450a296b..0355dd47af 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -9,6 +9,7 @@ #include "libqtest.h" #include "qemu/bitops.h" #include "qemu/bswap.h" +#include "crypto/cipher.h" #include "aspeed-hace-utils.h" /* @@ -645,3 +646,327 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, qtest_quit(s); } +/* + * Crypto engine register layout (offsets from the HACE base). + */ +#define HACE_CRYPTO_SRC 0x00 +#define HACE_CRYPTO_DEST 0x04 +#define HACE_CRYPTO_CONTEXT 0x08 +#define HACE_CRYPTO_DATA_LEN 0x0c +#define HACE_CRYPTO_CMD 0x10 + +/* Crypto command bits */ +#define HACE_CMD_ENCRYPT BIT(7) +#define HACE_CMD_ISR_EN BIT(12) +#define HACE_CMD_DES_SELECT BIT(16) +#define HACE_CMD_TRIPLE_DES BIT(17) +#define HACE_CMD_SRC_SG_CTRL BIT(18) +#define HACE_CMD_DST_SG_CTRL BIT(19) +#define HACE_CMD_OP_MODE_MASK (0x7 << 4) +#define HACE_CMD_ECB (0x0 << 4) +#define HACE_CMD_CBC (0x1 << 4) +#define HACE_CMD_AES128 (0x0 << 2) + +/* Context buffer layout: IV (DES at +8), key at +0x10 */ +#define HACE_CTX_KEY_OFFSET 0x10 +#define HACE_CTX_SIZE 0x30 + +/* + * Crypto known-answer test vectors, taken verbatim from the Linux kernel + * crypto self-test templates in crypto/testmgr.h: + * + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/crypto/testmgr.h?h=v6.18 + * + * The originating template is noted above each block. CTR and the longer CBC + * vectors are truncated to a single block (still a valid known-answer test as + * the first block only depends on the IV). + */ + +/* aes_tv_template[0] (FIPS-197) */ +static const uint8_t aes128_ecb_key[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; +static const uint8_t aes128_ecb_ptext[16] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; +static const uint8_t aes128_ecb_ctext[16] = { + 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, + 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a }; + +/* aes_cbc_tv_template[0] (RFC 3602) */ +static const uint8_t aes128_cbc_key[16] = { + 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, + 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 }; +static const uint8_t aes128_cbc_iv[16] = { + 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, + 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 }; +static const uint8_t aes128_cbc_ptext[16] = { + 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67 }; +static const uint8_t aes128_cbc_ctext[16] = { + 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }; +static const uint8_t aes128_cbc_ivout[16] = { + 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8, + 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a }; + +/* des_tv_template[0] (Applied Cryptography) */ +static const uint8_t des_ecb_key[8] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; +static const uint8_t des_ecb_ptext[8] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xe7 }; +static const uint8_t des_ecb_ctext[8] = { + 0xc9, 0x57, 0x44, 0x25, 0x6a, 0x5e, 0xd3, 0x1d }; + +/* des_cbc_tv_template[0] (OpenSSL), first block */ +static const uint8_t des_cbc_key[8] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef }; +static const uint8_t des_cbc_iv[8] = { + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }; +static const uint8_t des_cbc_ptext[8] = { + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x20 }; +static const uint8_t des_cbc_ctext[8] = { + 0xcc, 0xd1, 0x73, 0xff, 0xab, 0x20, 0x39, 0xf4 }; + +/* des3_ede_tv_template[0] (OpenSSL) */ +static const uint8_t tdes_ecb_key[24] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }; +static const uint8_t tdes_ecb_ptext[8] = { + 0x73, 0x6f, 0x6d, 0x65, 0x64, 0x61, 0x74, 0x61 }; +static const uint8_t tdes_ecb_ctext[8] = { + 0x18, 0xd7, 0x48, 0xe5, 0x63, 0x62, 0x05, 0x72 }; + +/* des3_ede_cbc_tv_template[0] (OpenSSL), first block */ +static const uint8_t tdes_cbc_key[24] = { + 0xe9, 0xc0, 0xff, 0x2e, 0x76, 0x0b, 0x64, 0x24, + 0x44, 0x4d, 0x99, 0x5a, 0x12, 0xd6, 0x40, 0xc0, + 0xea, 0xc2, 0x84, 0xe8, 0x14, 0x95, 0xdb, 0xe8 }; +static const uint8_t tdes_cbc_iv[8] = { + 0x7d, 0x33, 0x88, 0x93, 0x0f, 0x93, 0xb2, 0x42 }; +static const uint8_t tdes_cbc_ptext[8] = { + 0x6f, 0x54, 0x20, 0x6f, 0x61, 0x4d, 0x79, 0x6e }; +static const uint8_t tdes_cbc_ctext[8] = { + 0x0e, 0x2d, 0xb6, 0x97, 0x3c, 0x56, 0x33, 0xf4 }; + +typedef struct CryptTest { + QCryptoCipherMode mode; + QCryptoCipherAlgo alg; + /* expected context IV after encrypt, or NULL */ + const uint8_t *iv_out; + const uint8_t *ptext; + const uint8_t *ctext; + const uint8_t *key; + const uint8_t *iv; + const char *name; + size_t keylen; + /* algorithm | mode | key size selection */ + uint32_t cmd; + size_t ivlen; + size_t len; +} CryptTest; + +static const CryptTest crypt_tests[] = { + { + .name = "aes128-ecb", + .cmd = HACE_CMD_AES128 | HACE_CMD_ECB, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = aes128_ecb_key, + .keylen = sizeof(aes128_ecb_key), + .ptext = aes128_ecb_ptext, + .ctext = aes128_ecb_ctext, + .len = sizeof(aes128_ecb_ptext), + }, + { + .name = "aes128-cbc", + .cmd = HACE_CMD_AES128 | HACE_CMD_CBC, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = aes128_cbc_key, + .keylen = sizeof(aes128_cbc_key), + .iv = aes128_cbc_iv, + .ivlen = sizeof(aes128_cbc_iv), + .ptext = aes128_cbc_ptext, + .ctext = aes128_cbc_ctext, + .iv_out = aes128_cbc_ivout, + .len = sizeof(aes128_cbc_ptext), + }, + { + .name = "des-ecb", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_ECB, + .alg = QCRYPTO_CIPHER_ALGO_DES, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = des_ecb_key, + .keylen = sizeof(des_ecb_key), + .ptext = des_ecb_ptext, + .ctext = des_ecb_ctext, + .len = sizeof(des_ecb_ptext), + }, + { + .name = "des-cbc", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CBC, + .alg = QCRYPTO_CIPHER_ALGO_DES, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = des_cbc_key, + .keylen = sizeof(des_cbc_key), + .iv = des_cbc_iv, + .ivlen = sizeof(des_cbc_iv), + .ptext = des_cbc_ptext, + .ctext = des_cbc_ctext, + .len = sizeof(des_cbc_ptext), + }, + { + .name = "des3_ede-ecb", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_ECB, + .alg = QCRYPTO_CIPHER_ALGO_3DES, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = tdes_ecb_key, + .keylen = sizeof(tdes_ecb_key), + .ptext = tdes_ecb_ptext, + .ctext = tdes_ecb_ctext, + .len = sizeof(tdes_ecb_ptext), + }, + { + .name = "des3_ede-cbc", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_CBC, + .alg = QCRYPTO_CIPHER_ALGO_3DES, + .mode = QCRYPTO_CIPHER_MODE_CBC, + .key = tdes_cbc_key, + .keylen = sizeof(tdes_cbc_key), + .iv = tdes_cbc_iv, + .ivlen = sizeof(tdes_cbc_iv), + .ptext = tdes_cbc_ptext, + .ctext = tdes_cbc_ctext, + .len = sizeof(tdes_cbc_ptext), + }, +}; + +/* DRAM offsets for the crypto test source, destination and context buffers. */ +#define CRYPT_OFF_SRC 0x10000 +#define CRYPT_OFF_DST 0x20000 +#define CRYPT_OFF_CTX 0x30000 + +/* Describes one registered crypto test (qtest_add_data_func() data pointer). */ +typedef struct AspeedCryptoTest { + const char *machine; + uint64_t dram; + uint32_t base; + int index; +} AspeedCryptoTest; + +/* Map a command's operation mode (HACE10[6:4]) to a CRYPT_MODE_* flag. */ +static uint32_t crypt_mode_flag(uint32_t cmd) +{ + switch (cmd & HACE_CMD_OP_MODE_MASK) { + case HACE_CMD_ECB: + return CRYPT_MODE_ECB; + case HACE_CMD_CBC: + return CRYPT_MODE_CBC; + default: + return 0; + } +} + +static void crypt_write_ctx(QTestState *s, uint64_t ctx_addr, + const CryptTest *t) +{ + size_t iv_off = (t->cmd & HACE_CMD_DES_SELECT) ? 8 : 0; + uint8_t ctx[HACE_CTX_SIZE] = { 0 }; + + if (t->iv) { + memcpy(ctx + iv_off, t->iv, t->ivlen); + } + memcpy(ctx + HACE_CTX_KEY_OFFSET, t->key, t->keylen); + qtest_memwrite(s, ctx_addr, ctx, sizeof(ctx)); +} + +/* Run one crypto operation in direct access mode and read back the result. */ +static void crypt_run_direct(QTestState *s, uint32_t base, uint64_t dram, + const CryptTest *t, bool encrypt, uint8_t *out) +{ + const uint8_t *in = encrypt ? t->ptext : t->ctext; + uint32_t cmd = t->cmd | HACE_CMD_ISR_EN; + uint64_t src = dram + CRYPT_OFF_SRC; + uint64_t dst = dram + CRYPT_OFF_DST; + uint64_t ctx = dram + CRYPT_OFF_CTX; + + if (encrypt) { + cmd |= HACE_CMD_ENCRYPT; + } + + crypt_write_ctx(s, ctx, t); + qtest_memwrite(s, src, in, t->len); + + qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src); + qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst); + qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx); + qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len); + qtest_writel(s, base + HACE_CRYPTO_CMD, cmd); + + g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==, + HACE_CRYPTO_ISR); + qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR); + + qtest_memread(s, dst, out, t->len); +} + +static void aspeed_test_crypto_direct(const void *data) +{ + const AspeedCryptoTest *c = data; + const CryptTest *t = &crypt_tests[c->index]; + QTestState *s = qtest_init(c->machine); + uint8_t out[64]; + uint8_t iv[16]; + size_t iv_off; + + g_assert_cmpuint(t->len, <=, sizeof(out)); + + /* Encrypt: ptext -> ctext */ + crypt_run_direct(s, c->base, c->dram, t, true, out); + g_assert_cmpmem(out, t->len, t->ctext, t->len); + + if (t->iv_out) { + iv_off = (t->cmd & HACE_CMD_DES_SELECT) ? 8 : 0; + qtest_memread(s, c->dram + CRYPT_OFF_CTX + iv_off, iv, t->ivlen); + g_assert_cmpmem(iv, t->ivlen, t->iv_out, t->ivlen); + } + + /* Decrypt: ctext -> ptext */ + crypt_run_direct(s, c->base, c->dram, t, false, out); + g_assert_cmpmem(out, t->len, t->ptext, t->len); + + qtest_quit(s); +} + +void aspeed_add_crypto_tests(const char *prefix, const char *machine, + uint32_t base, uint64_t dram, uint32_t modes) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(crypt_tests); i++) { + g_autofree char *path = NULL; + AspeedCryptoTest *t; + + if (!(modes & crypt_mode_flag(crypt_tests[i].cmd))) { + continue; + } + + if (!qcrypto_cipher_supports(crypt_tests[i].alg, + crypt_tests[i].mode)) { + g_printerr("# skip unsupported %s\n", crypt_tests[i].name); + continue; + } + + path = g_strdup_printf("%s/hace/crypto/%s", prefix, + crypt_tests[i].name); + t = g_new0(AspeedCryptoTest, 1); + t->machine = machine; + t->base = base; + t->dram = dram; + t->index = i; + qtest_add_data_func_full(path, t, aspeed_test_crypto_direct, g_free); + } +} + diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index 27ab2bb975..13feaa61e4 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -79,5 +79,21 @@ void aspeed_test_sha512_accum(const char *machine, const uint32_t base, void aspeed_test_addresses(const char *machine, const uint32_t base, const struct AspeedMasks *expected); +/* + * Cipher modes a SoC's crypto engine supports, for aspeed_add_crypto_tests(). + */ +enum { + CRYPT_MODE_ECB = 1 << 0, + CRYPT_MODE_CBC = 1 << 1, +}; + +/* + * Register the crypto known-answer tests that @modes selects (a mask of + * CRYPT_MODE_*) for the given machine. Each test is named + * "/hace/crypto/". + */ +void aspeed_add_crypto_tests(const char *prefix, const char *machine, + uint32_t base, uint64_t dram, uint32_t modes); + #endif /* TESTS_ASPEED_HACE_UTILS_H */ diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index 38777020ca..4cb4c475e9 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -229,6 +229,12 @@ int main(int argc, char **argv) qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500); qtest_add_func("ast2500/hace/md5", test_md5_ast2500); + /* + * The AST2500 crypto engine uses direct access mode and supports ECB/CBC. + */ + aspeed_add_crypto_tests("ast2500", "-machine ast2500-evb", 0x1e6e3000, + 0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC); + qtest_add_func("ast2400/hace/addresses", test_addresses_ast2400); qtest_add_func("ast2400/hace/sha512", test_sha512_ast2400); qtest_add_func("ast2400/hace/sha256", test_sha256_ast2400); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 4d81857174..dcdbfe2889 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -392,9 +392,11 @@ if get_option('replication').allowed() endif qtests = { - 'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), + 'aspeed_hace-test': [files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), + crypto], 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), - 'ast2700-hace-test': files('aspeed-hace-utils.c', 'ast2700-hace-test.c'), + 'ast2700-hace-test': [files('aspeed-hace-utils.c', 'ast2700-hace-test.c'), + crypto], 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), From 604cec223c4bf20b8fef7788263b13d13fed3708 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:21 +0000 Subject: [PATCH 375/860] hw/misc/aspeed_hace: Support scatter-gather mode for the crypto command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2600 and later crypto engines drive the source and destination through scatter-gather lists (HACE10[18]/[19]) rather than the single contiguous buffers used by the AST2500 direct access mode. Each SG list entry is a length word (SG_LIST_LEN_LAST marks the final entry) followed by a DRAM address, matching the hash engine layout. Add a crypt_prepare_sg() helper that gathers the source into / scatters the destination out of the bounce buffer by walking the SG list, and select it or the existing crypt_prepare_direct() from do_crypt_operation based on HACE10[18], mirroring the hash engine's direct/scatter-gather dispatch. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 70 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 0f35d74137..646ff50476 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -608,13 +608,58 @@ static bool crypt_prepare_direct(AspeedHACEState *s, uint64_t addr, } /* - * Perform an AES/DES/3DES ECB/CBC operation in direct access mode: the source - * and destination are single contiguous buffers (HACE00/HACE04) and the IV/key - * come from the context buffer (HACE08). For CBC the resulting chaining IV is - * written back to the context buffer so the driver can continue the chain. + * Scatter-gather mode: the source/destination register points at an SG list + * whose entries are a length word (SG_LIST_LEN_LAST flags the final entry) + * followed by a DRAM address, matching the hash engine layout. Gather @len + * bytes into @buf, or scatter @buf back out when @to_dram is true. + * Returns true on success. + */ +static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr, + uint8_t *buf, uint32_t len, bool to_dram) +{ + uint32_t copied = 0; + uint32_t sg_addr; + uint32_t sg_len; + uint32_t entry; + int i; + + for (i = 0; i < ASPEED_HACE_MAX_SG && copied < len; i++) { + entry = address_space_ldl_le(&s->dram_as, addr, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_addr = address_space_ldl_le(&s->dram_as, addr + SG_LIST_LEN_SIZE, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_len = entry & SG_LIST_LEN_MASK; + + sg_addr &= SG_LIST_ADDR_MASK; + addr += SG_LIST_ENTRY_SIZE; + + if (sg_len > len - copied) { + sg_len = len - copied; + } + if (address_space_rw(&s->dram_as, sg_addr, MEMTXATTRS_UNSPECIFIED, + buf + copied, sg_len, to_dram)) { + return false; + } + copied += sg_len; + + if (entry & SG_LIST_LEN_LAST) { + break; + } + } + + return copied == len; +} + +/* + * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are + * either single contiguous buffers (direct access mode) or scatter-gather + * lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the IV/key come from + * the context buffer (HACE08). For CBC the resulting chaining IV is written + * back to the context buffer so the driver can continue the chain. */ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) { + bool sg_mode = cmd & CRYPT_CMD_SRC_SG_CTRL; uint32_t len = s->regs[R_CRYPT_DATA_LEN]; bool encrypt = cmd & CRYPT_CMD_ENCRYPT; g_autoptr(QCryptoCipher) cipher = NULL; @@ -631,6 +676,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) size_t iv_offset; size_t blocklen; size_t keylen; + bool status; if (len == 0) { return; @@ -684,8 +730,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) src_buf = g_malloc0(len); dst_buf = g_malloc0(len); + /* Gather the source into the bounce buffer, per the selected mode. */ src_addr = s->regs[R_CRYPT_SRC]; - if (!crypt_prepare_direct(s, src_addr, src_buf, len, false)) { + if (sg_mode) { + status = crypt_prepare_sg(s, src_addr, src_buf, len, false); + } else { + status = crypt_prepare_direct(s, src_addr, src_buf, len, false); + } + if (!status) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read src, addr=0x%" HWADDR_PRIx "\n", __func__, src_addr); @@ -714,8 +766,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) } } + /* Scatter the result back out, per the selected mode. */ dst_addr = s->regs[R_CRYPT_DEST]; - if (!crypt_prepare_direct(s, dst_addr, dst_buf, len, true)) { + if (sg_mode) { + status = crypt_prepare_sg(s, dst_addr, dst_buf, len, true); + } else { + status = crypt_prepare_direct(s, dst_addr, dst_buf, len, true); + } + if (!status) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write dst, addr=0x%" HWADDR_PRIx "\n", __func__, dst_addr); From 33399898b820f34f679f6740b1e0d73993e5d034 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:22 +0000 Subject: [PATCH 376/860] hw/misc/aspeed_hace: Support the CTR mode for the crypto command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2600, AST1030 and later crypto engines add AES/DES/3DES CTR mode (HACE10[6:4] = 0b100) on top of the ECB/CBC modes shared with the AST2500. Decode the CTR selection, round the working buffers up to a whole block so the stream-like final block is still processed a block at a time, and write the counter advanced by the number of blocks consumed back to the context buffer so the driver can continue across requests. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 50 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 646ff50476..d84f7f9cec 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -41,6 +41,7 @@ #define CRYPT_CMD_OP_MODE_MASK (0x7 << 4) #define CRYPT_CMD_ECB (0x0 << 4) #define CRYPT_CMD_CBC (0x1 << 4) +#define CRYPT_CMD_CTR (0x4 << 4) /* AES key length HACE10[3:2] */ #define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2) #define CRYPT_CMD_AES256 (0x2 << 2) @@ -587,6 +588,9 @@ static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg, case CRYPT_CMD_CBC: *mode = QCRYPTO_CIPHER_MODE_CBC; break; + case CRYPT_CMD_CTR: + *mode = QCRYPTO_CIPHER_MODE_CTR; + break; default: return false; } @@ -650,6 +654,22 @@ static bool crypt_prepare_sg(AspeedHACEState *s, uint64_t addr, return copied == len; } +/* + * Add @add to the big-endian counter block @ctr (@len bytes) in place, so the + * CTR mode counter can be advanced by the number of blocks just consumed. + */ +static void crypt_be_add(uint8_t *ctr, size_t len, uint64_t add) +{ + size_t i = len; + + while (i > 0 && add) { + i--; + add += ctr[i]; + ctr[i] = add & 0xff; + add >>= 8; + } +} + /* * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are * either single contiguous buffers (direct access mode) or scatter-gather @@ -675,6 +695,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) uint64_t dst_addr; size_t iv_offset; size_t blocklen; + size_t buf_len; size_t keylen; bool status; @@ -727,8 +748,14 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) return; } - src_buf = g_malloc0(len); - dst_buf = g_malloc0(len); + /* + * Round the working buffers up to a whole block. Block modes are already + * block-aligned; the stream-like CTR mode may leave a partial final block + * that the engine still processes a full block at a time. + */ + buf_len = QEMU_ALIGN_UP(len, blocklen); + src_buf = g_malloc0(buf_len); + dst_buf = g_malloc0(buf_len); /* Gather the source into the bounce buffer, per the selected mode. */ src_addr = s->regs[R_CRYPT_SRC]; @@ -749,7 +776,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) } if (encrypt) { - if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, len, + if (qcrypto_cipher_encrypt(cipher, src_buf, dst_buf, buf_len, &local_err) < 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: encrypt failed: %s\n", __func__, error_get_pretty(local_err)); @@ -757,7 +784,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) return; } } else { - if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, len, + if (qcrypto_cipher_decrypt(cipher, src_buf, dst_buf, buf_len, &local_err) < 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: decrypt failed: %s\n", __func__, error_get_pretty(local_err)); @@ -790,13 +817,26 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) * output when encrypting, or of the input when decrypting. Write it * back as the IV for the next request. */ - next_iv = (encrypt ? dst_buf : src_buf) + len - blocklen; + next_iv = (encrypt ? dst_buf : src_buf) + buf_len - blocklen; if (address_space_write(&s->dram_as, ctx_addr + iv_offset, MEMTXATTRS_UNSPECIFIED, next_iv, blocklen)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n", __func__, ctx_addr + iv_offset); } + } else if (mode == QCRYPTO_CIPHER_MODE_CTR) { + /* + * CTR chains on the counter, which advances by one per block. Add the + * number of blocks processed (buf_len / blocklen) and write it back. + */ + crypt_be_add(ctx + iv_offset, blocklen, buf_len / blocklen); + if (address_space_write(&s->dram_as, ctx_addr + iv_offset, + MEMTXATTRS_UNSPECIFIED, ctx + iv_offset, + blocklen)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n", + __func__, ctx_addr + iv_offset); + } } } From 48274cdb830f30bc573e089b94d0a061d7235081 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:23 +0000 Subject: [PATCH 377/860] tests/qtest/aspeed-hace: Test the crypto command on the AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the crypto known-answer tests to cover the AST2600 crypto engine, which drives the source and destination through scatter-gather lists and adds CTR mode on top of the ECB/CBC modes shared with the AST2500. Add a scatter-gather runner that describes each buffer with three non-adjacent fragments to exercise the gather/scatter path, add AES/DES/3DES CTR vectors (verifying the counter written back to the context buffer), and give aspeed_add_crypto_tests() a mode mask and a scatter-gather flag so each SoC registers exactly the modes and transfer method it supports. Register the AST2600 with ECB/CBC/CTR in scatter-gather mode. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 217 +++++++++++++++++++++++++++++++- tests/qtest/aspeed-hace-utils.h | 7 +- tests/qtest/aspeed_hace-test.c | 8 +- 3 files changed, 224 insertions(+), 8 deletions(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 0355dd47af..f582c88ef5 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -665,6 +665,7 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, #define HACE_CMD_OP_MODE_MASK (0x7 << 4) #define HACE_CMD_ECB (0x0 << 4) #define HACE_CMD_CBC (0x1 << 4) +#define HACE_CMD_CTR (0x4 << 4) #define HACE_CMD_AES128 (0x0 << 2) /* Context buffer layout: IV (DES at +8), key at +0x10 */ @@ -750,6 +751,49 @@ static const uint8_t tdes_cbc_ptext[8] = { static const uint8_t tdes_cbc_ctext[8] = { 0x0e, 0x2d, 0xb6, 0x97, 0x3c, 0x56, 0x33, 0xf4 }; +/* aes_ctr_tv_template[0] (NIST SP800-38A F.5.1), first block */ +static const uint8_t aes128_ctr_key[16] = { + 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; +static const uint8_t aes128_ctr_iv[16] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; +static const uint8_t aes128_ctr_ptext[16] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a }; +static const uint8_t aes128_ctr_ctext[16] = { + 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, + 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce }; +static const uint8_t aes128_ctr_ivout[16] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xff, 0x00 }; + +/* des_ctr_tv_template[0] (Crypto++), first block */ +static const uint8_t des_ctr_key[8] = { + 0xc9, 0x83, 0xa6, 0xc9, 0xec, 0x0f, 0x32, 0x55 }; +static const uint8_t des_ctr_iv[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd }; +static const uint8_t des_ctr_ptext[8] = { + 0x50, 0xb9, 0x22, 0xae, 0x17, 0x80, 0x0c, 0x75 }; +static const uint8_t des_ctr_ctext[8] = { + 0x2f, 0x96, 0x06, 0x0f, 0x50, 0xc9, 0x68, 0x03 }; +static const uint8_t des_ctr_ivout[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe }; + +/* des3_ede_ctr_tv_template[0] (Crypto++), first block */ +static const uint8_t tdes_ctr_key[24] = { + 0x9c, 0xd6, 0xf3, 0x9c, 0xb9, 0x5a, 0x67, 0x00, + 0x5a, 0x67, 0x00, 0x2d, 0xce, 0xeb, 0x2d, 0xce, + 0xeb, 0xb4, 0x51, 0x72, 0xb4, 0x51, 0x72, 0x1f }; +static const uint8_t tdes_ctr_iv[8] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +static const uint8_t tdes_ctr_ptext[8] = { + 0x05, 0xec, 0x77, 0xfb, 0x42, 0xd5, 0x59, 0x20 }; +static const uint8_t tdes_ctr_ctext[8] = { + 0x07, 0xc2, 0x08, 0x20, 0x72, 0x1f, 0x49, 0xef }; +static const uint8_t tdes_ctr_ivout[8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + typedef struct CryptTest { QCryptoCipherMode mode; QCryptoCipherAlgo alg; @@ -841,12 +885,65 @@ static const CryptTest crypt_tests[] = { .ctext = tdes_cbc_ctext, .len = sizeof(tdes_cbc_ptext), }, + { + .name = "aes128-ctr", + .cmd = HACE_CMD_AES128 | HACE_CMD_CTR, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = aes128_ctr_key, + .keylen = sizeof(aes128_ctr_key), + .iv = aes128_ctr_iv, + .ivlen = sizeof(aes128_ctr_iv), + .ptext = aes128_ctr_ptext, + .ctext = aes128_ctr_ctext, + .iv_out = aes128_ctr_ivout, + .len = sizeof(aes128_ctr_ptext), + }, + { + .name = "des-ctr", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_CTR, + .alg = QCRYPTO_CIPHER_ALGO_DES, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = des_ctr_key, + .keylen = sizeof(des_ctr_key), + .iv = des_ctr_iv, + .ivlen = sizeof(des_ctr_iv), + .ptext = des_ctr_ptext, + .ctext = des_ctr_ctext, + .iv_out = des_ctr_ivout, + .len = sizeof(des_ctr_ptext), + }, + { + .name = "des3_ede-ctr", + .cmd = HACE_CMD_DES_SELECT | HACE_CMD_TRIPLE_DES | HACE_CMD_CTR, + .alg = QCRYPTO_CIPHER_ALGO_3DES, + .mode = QCRYPTO_CIPHER_MODE_CTR, + .key = tdes_ctr_key, + .keylen = sizeof(tdes_ctr_key), + .iv = tdes_ctr_iv, + .ivlen = sizeof(tdes_ctr_iv), + .ptext = tdes_ctr_ptext, + .ctext = tdes_ctr_ctext, + .iv_out = tdes_ctr_ivout, + .len = sizeof(tdes_ctr_ptext), + }, }; /* DRAM offsets for the crypto test source, destination and context buffers. */ #define CRYPT_OFF_SRC 0x10000 #define CRYPT_OFF_DST 0x20000 #define CRYPT_OFF_CTX 0x30000 +/* Scatter-gather list offsets (each list has CRYPT_SG_FRAGS entries). */ +#define CRYPT_OFF_SRC_SG 0x40000 +#define CRYPT_OFF_DST_SG 0x50000 +/* + * The scatter-gather tests split each buffer into CRYPT_SG_FRAGS fragments, + * each placed CRYPT_SG_FRAG_STRIDE apart so the fragments never abut. The gaps + * make the test fail if the engine ignores the list and reads one contiguous + * block. + */ +#define CRYPT_SG_FRAGS 3 +#define CRYPT_SG_FRAG_STRIDE 0x1000 /* Describes one registered crypto test (qtest_add_data_func() data pointer). */ typedef struct AspeedCryptoTest { @@ -854,6 +951,7 @@ typedef struct AspeedCryptoTest { uint64_t dram; uint32_t base; int index; + bool sg; } AspeedCryptoTest; /* Map a command's operation mode (HACE10[6:4]) to a CRYPT_MODE_* flag. */ @@ -864,6 +962,8 @@ static uint32_t crypt_mode_flag(uint32_t cmd) return CRYPT_MODE_ECB; case HACE_CMD_CBC: return CRYPT_MODE_CBC; + case HACE_CMD_CTR: + return CRYPT_MODE_CTR; default: return 0; } @@ -912,7 +1012,104 @@ static void crypt_run_direct(QTestState *s, uint32_t base, uint64_t dram, qtest_memread(s, dst, out, t->len); } -static void aspeed_test_crypto_direct(const void *data) +/* + * Byte range [*frag_off, *frag_off + *frag_len) of fragment @index when an + * @len-byte buffer is split into CRYPT_SG_FRAGS pieces; the last piece takes + * the remainder of an uneven split. + */ +static void crypt_frag_range(uint32_t len, int index, + uint32_t *frag_off, uint32_t *frag_len) +{ + uint32_t base = len / CRYPT_SG_FRAGS; + + *frag_off = base * index; + *frag_len = (index == CRYPT_SG_FRAGS - 1) ? len - *frag_off : base; +} + +/* + * Scatter [in, len) across CRYPT_SG_FRAGS buffers based at @base_off and spaced + * CRYPT_SG_FRAG_STRIDE apart, then build the SG list describing them at @list. + * When @in is NULL only the list is built (used for the destination, which the + * engine fills in). + */ +static void crypt_make_sg(QTestState *s, uint64_t dram, uint32_t base_off, + uint64_t list, const uint8_t *in, uint32_t len) +{ + struct AspeedSgList sg[CRYPT_SG_FRAGS]; + uint32_t frag_off; + uint32_t frag_len; + uint64_t buf; + int i; + + for (i = 0; i < CRYPT_SG_FRAGS; i++) { + crypt_frag_range(len, i, &frag_off, &frag_len); + buf = dram + base_off + i * CRYPT_SG_FRAG_STRIDE; + + if (in) { + qtest_memwrite(s, buf, in + frag_off, frag_len); + } + sg[i].len = cpu_to_le32(frag_len | (i == CRYPT_SG_FRAGS - 1 ? + SG_LIST_LEN_LAST : 0)); + sg[i].addr = cpu_to_le32((uint32_t)buf); + } + + qtest_memwrite(s, list, sg, sizeof(sg)); +} + +/* Gather a scatter-gathered result back from the CRYPT_SG_FRAGS buffers. */ +static void crypt_gather_sg(QTestState *s, uint64_t dram, uint32_t base_off, + uint8_t *out, uint32_t len) +{ + uint32_t frag_off; + uint32_t frag_len; + int i; + + for (i = 0; i < CRYPT_SG_FRAGS; i++) { + crypt_frag_range(len, i, &frag_off, &frag_len); + qtest_memread(s, dram + base_off + i * CRYPT_SG_FRAG_STRIDE, + out + frag_off, frag_len); + } +} + +/* + * Run one block-cipher (ECB/CBC/CTR) operation in scatter-gather mode and read + * back the result. The source and destination are each split across + * CRYPT_SG_FRAGS non-adjacent DRAM buffers described by an SG list; the gaps + * ensure the test fails if the engine ignores the list and reads one + * contiguous block. + */ +static void crypt_run_sg(QTestState *s, uint32_t base, uint64_t dram, + const CryptTest *t, bool encrypt, uint8_t *out) +{ + const uint8_t *in = encrypt ? t->ptext : t->ctext; + uint64_t src_sg = dram + CRYPT_OFF_SRC_SG; + uint64_t dst_sg = dram + CRYPT_OFF_DST_SG; + uint64_t ctx = dram + CRYPT_OFF_CTX; + uint32_t cmd = t->cmd | HACE_CMD_ISR_EN | HACE_CMD_SRC_SG_CTRL | + HACE_CMD_DST_SG_CTRL; + + if (encrypt) { + cmd |= HACE_CMD_ENCRYPT; + } + + crypt_write_ctx(s, ctx, t); + crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len); + crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len); + + qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg); + qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg); + qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx); + qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len); + qtest_writel(s, base + HACE_CRYPTO_CMD, cmd); + + g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==, + HACE_CRYPTO_ISR); + qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR); + + crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len); +} + +static void aspeed_test_crypto(const void *data) { const AspeedCryptoTest *c = data; const CryptTest *t = &crypt_tests[c->index]; @@ -924,7 +1121,11 @@ static void aspeed_test_crypto_direct(const void *data) g_assert_cmpuint(t->len, <=, sizeof(out)); /* Encrypt: ptext -> ctext */ - crypt_run_direct(s, c->base, c->dram, t, true, out); + if (c->sg) { + crypt_run_sg(s, c->base, c->dram, t, true, out); + } else { + crypt_run_direct(s, c->base, c->dram, t, true, out); + } g_assert_cmpmem(out, t->len, t->ctext, t->len); if (t->iv_out) { @@ -934,14 +1135,19 @@ static void aspeed_test_crypto_direct(const void *data) } /* Decrypt: ctext -> ptext */ - crypt_run_direct(s, c->base, c->dram, t, false, out); + if (c->sg) { + crypt_run_sg(s, c->base, c->dram, t, false, out); + } else { + crypt_run_direct(s, c->base, c->dram, t, false, out); + } g_assert_cmpmem(out, t->len, t->ptext, t->len); qtest_quit(s); } void aspeed_add_crypto_tests(const char *prefix, const char *machine, - uint32_t base, uint64_t dram, uint32_t modes) + uint32_t base, uint64_t dram, uint32_t modes, + bool sg) { int i; @@ -966,7 +1172,8 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine, t->base = base; t->dram = dram; t->index = i; - qtest_add_data_func_full(path, t, aspeed_test_crypto_direct, g_free); + t->sg = sg; + qtest_add_data_func_full(path, t, aspeed_test_crypto, g_free); } } diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index 13feaa61e4..82b0b3f93d 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -85,15 +85,18 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, enum { CRYPT_MODE_ECB = 1 << 0, CRYPT_MODE_CBC = 1 << 1, + CRYPT_MODE_CTR = 1 << 2, }; /* * Register the crypto known-answer tests that @modes selects (a mask of * CRYPT_MODE_*) for the given machine. Each test is named - * "/hace/crypto/". + * "/hace/crypto/". @sg selects scatter-gather mode (used by the + * AST2600 and later) instead of the AST2500 direct access mode. */ void aspeed_add_crypto_tests(const char *prefix, const char *machine, - uint32_t base, uint64_t dram, uint32_t modes); + uint32_t base, uint64_t dram, uint32_t modes, + bool sg); #endif /* TESTS_ASPEED_HACE_UTILS_H */ diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index 4cb4c475e9..61a3e3feb5 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -224,6 +224,12 @@ int main(int argc, char **argv) qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600); qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600); + /* The AST2600 crypto engine uses scatter-gather mode and adds CTR. */ + aspeed_add_crypto_tests("ast2600", "-machine ast2600-evb", 0x1e6d0000, + 0x80000000, + CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR, + true); + qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500); qtest_add_func("ast2500/hace/sha512", test_sha512_ast2500); qtest_add_func("ast2500/hace/sha256", test_sha256_ast2500); @@ -233,7 +239,7 @@ int main(int argc, char **argv) * The AST2500 crypto engine uses direct access mode and supports ECB/CBC. */ aspeed_add_crypto_tests("ast2500", "-machine ast2500-evb", 0x1e6e3000, - 0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC); + 0x80000000, CRYPT_MODE_ECB | CRYPT_MODE_CBC, false); qtest_add_func("ast2400/hace/addresses", test_addresses_ast2400); qtest_add_func("ast2400/hace/sha512", test_sha512_ast2400); From d030f3cabe74e0b3205b417ca061c3ac6e7a4334 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:24 +0000 Subject: [PATCH 378/860] tests/qtest/aspeed-hace: Test the crypto command on the AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST1030 reuses the AST2600 crypto engine, so it drives the same scatter-gather transfers and supports the same ECB/CBC/CTR modes. Reuse the crypto known-answer tests to cover it, registering the AST1030 with the same modes and scatter-gather flag as the AST2600. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_hace-test.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index 61a3e3feb5..42130df1e2 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -210,6 +210,12 @@ int main(int argc, char **argv) qtest_add_func("ast1030/hace/sha384_accum", test_sha384_accum_ast1030); qtest_add_func("ast1030/hace/sha256_accum", test_sha256_accum_ast1030); + /* The AST1030 reuses the AST2600 crypto engine (scatter-gather, CTR). */ + aspeed_add_crypto_tests("ast1030", "-machine ast1030-evb", 0x7e6d0000, + 0x00000000, + CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR, + true); + qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600); qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600); qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600); From b781906fccf1e0668255f73fa9d96670a4f91ddb Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:26 +0000 Subject: [PATCH 379/860] crypto/cipher: Add GCM to QCryptoCipherMode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the GCM cipher mode so authenticated encryption can be built on top of the existing qcrypto_cipher API. GCM is an IV-based mode, so register it in mode_need_iv. No backend advertises it yet, so it stays unsupported until a backend and the AAD/tag helpers are added in the following patches. Signed-off-by: Jamin Lin Reviewed-by: Daniel P. Berrangé Acked-by: Daniel P. Berrangé Acked-by: Markus Armbruster Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- crypto/cipher.c | 1 + qapi/crypto.json | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/crypto/cipher.c b/crypto/cipher.c index 229710f76b..64c2339d17 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -66,6 +66,7 @@ static const bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = { [QCRYPTO_CIPHER_MODE_CBC] = true, [QCRYPTO_CIPHER_MODE_XTS] = true, [QCRYPTO_CIPHER_MODE_CTR] = true, + [QCRYPTO_CIPHER_MODE_GCM] = true, }; diff --git a/qapi/crypto.json b/qapi/crypto.json index 2b55befef9..6e3a98ff68 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -121,10 +121,12 @@ # # @ctr: Counter (Since 2.8) # +# @gcm: Galois/Counter Mode (Since 11.2) +# # Since: 2.6 ## { 'enum': 'QCryptoCipherMode', - 'data': ['ecb', 'cbc', 'xts', 'ctr']} + 'data': ['ecb', 'cbc', 'xts', 'ctr', 'gcm']} ## # @QCryptoIVGenAlgo: From a4383c086228f8626f2318b7f4b984002ce820df Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:27 +0000 Subject: [PATCH 380/860] crypto/cipher: Add setaad/gettag for AEAD modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AEAD modes such as GCM authenticate optional associated data (AAD) and produce an authentication tag, which the block-cipher encrypt/decrypt interface cannot express. Add qcrypto_cipher_setaad() and qcrypto_cipher_gettag() plus the matching backend driver hooks. The generic front-end reports an error when the selected mode's driver does not implement them, so calling them on a non-AEAD mode fails cleanly. Signed-off-by: Jamin Lin Reviewed-by: Daniel P. Berrangé Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- crypto/cipher.c | 31 +++++++++++++++++++++++++++++++ crypto/cipherpriv.h | 8 ++++++++ include/crypto/cipher.h | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/crypto/cipher.c b/crypto/cipher.c index 64c2339d17..1dc912b217 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -205,6 +205,37 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, } +int qcrypto_cipher_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + const QCryptoCipherDriver *drv = cipher->driver; + + if (!drv->cipher_setaad) { + error_setg(errp, "The cipher mode does not support associated data"); + return -1; + } + + return drv->cipher_setaad(cipher, aad, len, errp); +} + + +int qcrypto_cipher_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp) +{ + const QCryptoCipherDriver *drv = cipher->driver; + + if (!drv->cipher_gettag) { + error_setg(errp, + "The cipher mode does not produce an authentication tag"); + return -1; + } + + return drv->cipher_gettag(cipher, tag, len, errp); +} + + void qcrypto_cipher_free(QCryptoCipher *cipher) { if (cipher) { diff --git a/crypto/cipherpriv.h b/crypto/cipherpriv.h index 64737ce961..c4f995f369 100644 --- a/crypto/cipherpriv.h +++ b/crypto/cipherpriv.h @@ -34,6 +34,14 @@ struct QCryptoCipherDriver { const uint8_t *iv, size_t niv, Error **errp); + int (*cipher_setaad)(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp); + + int (*cipher_gettag)(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp); + void (*cipher_free)(QCryptoCipher *cipher); }; diff --git a/include/crypto/cipher.h b/include/crypto/cipher.h index 92939310ef..2e361411b9 100644 --- a/include/crypto/cipher.h +++ b/include/crypto/cipher.h @@ -235,4 +235,40 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher, const uint8_t *iv, size_t niv, Error **errp); +/** + * qcrypto_cipher_setaad: + * @cipher: the cipher object + * @aad: the associated data to authenticate + * @len: the length of @aad + * @errp: pointer to a NULL-initialized error object + * + * For AEAD modes such as GCM, feed the associated data (AAD) that is + * authenticated but not encrypted. It must be called after + * qcrypto_cipher_setiv() and before the first encrypt/decrypt call. It is + * an error to call this on a mode that is not an AEAD mode. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_cipher_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp); + +/** + * qcrypto_cipher_gettag: + * @cipher: the cipher object + * @tag: buffer to receive the authentication tag + * @len: the length of @tag + * @errp: pointer to a NULL-initialized error object + * + * For AEAD modes such as GCM, read back the authentication tag computed + * over the associated data and the message. It must be called after the + * encrypt/decrypt operation. It is an error to call this on a mode that is + * not an AEAD mode. + * + * Returns: 0 on success, -1 on error + */ +int qcrypto_cipher_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp); + #endif /* QCRYPTO_CIPHER_H */ From 1429ef61c89672ef8c8eb3241c22ba46390b9003 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:28 +0000 Subject: [PATCH 381/860] crypto/cipher-gcrypt: Implement AES-GCM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Map QCRYPTO_CIPHER_MODE_GCM to GCRY_CIPHER_MODE_GCM and advertise it in qcrypto_cipher_supports() for 128-bit block ciphers. Add a GCM driver whose setiv accepts the (typically 96-bit) nonce, whose encrypt/decrypt do not require block-aligned lengths, and which implements setaad via gcry_cipher_authenticate() and gettag via gcry_cipher_gettag(). Signed-off-by: Jamin Lin Reviewed-by: Daniel P. Berrangé Acked-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- crypto/cipher-gcrypt.c.inc | 101 +++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/crypto/cipher-gcrypt.c.inc b/crypto/cipher-gcrypt.c.inc index 12eb9ddb5a..fce09a3c77 100644 --- a/crypto/cipher-gcrypt.c.inc +++ b/crypto/cipher-gcrypt.c.inc @@ -65,6 +65,8 @@ static int qcrypto_cipher_mode_to_gcry_mode(QCryptoCipherMode mode) return GCRY_CIPHER_MODE_CBC; case QCRYPTO_CIPHER_MODE_CTR: return GCRY_CIPHER_MODE_CTR; + case QCRYPTO_CIPHER_MODE_GCM: + return GCRY_CIPHER_MODE_GCM; default: return GCRY_CIPHER_MODE_NONE; } @@ -104,6 +106,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, case QCRYPTO_CIPHER_MODE_XTS: case QCRYPTO_CIPHER_MODE_CTR: return true; + case QCRYPTO_CIPHER_MODE_GCM: + /* GCM requires a 128-bit block cipher. */ + return gcry_cipher_get_algo_blklen( + qcrypto_cipher_alg_to_gcry_alg(alg)) == 16; default: return false; } @@ -228,6 +234,99 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = { .cipher_free = qcrypto_gcrypt_ctx_free, }; +/* + * GCM is an AEAD stream mode: the IV/nonce need not match the block size, + * the message length need not be a multiple of the block size, associated + * data is fed with gcry_cipher_authenticate() and the authentication tag is + * read back with gcry_cipher_gettag(). + */ +static int qcrypto_gcrypt_gcm_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + gcry_cipher_reset(ctx->handle); + err = gcry_cipher_setiv(ctx->handle, iv, niv); + if (err != 0) { + error_setg(errp, "Cannot set IV: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_authenticate(ctx->handle, aad, len); + if (err != 0) { + error_setg(errp, "Cannot set AAD: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_encrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_encrypt(ctx->handle, out, len, in, len); + if (err != 0) { + error_setg(errp, "Cannot encrypt data: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_decrypt(QCryptoCipher *cipher, const void *in, + void *out, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_decrypt(ctx->handle, out, len, in, len); + if (err != 0) { + error_setg(errp, "Cannot decrypt data: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static int qcrypto_gcrypt_gcm_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, Error **errp) +{ + QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base); + gcry_error_t err; + + err = gcry_cipher_gettag(ctx->handle, tag, len); + if (err != 0) { + error_setg(errp, "Cannot get tag: %s", gcry_strerror(err)); + return -1; + } + + return 0; +} + +static const struct QCryptoCipherDriver qcrypto_gcrypt_gcm_driver = { + .cipher_encrypt = qcrypto_gcrypt_gcm_encrypt, + .cipher_decrypt = qcrypto_gcrypt_gcm_decrypt, + .cipher_setiv = qcrypto_gcrypt_gcm_setiv, + .cipher_setaad = qcrypto_gcrypt_gcm_setaad, + .cipher_gettag = qcrypto_gcrypt_gcm_gettag, + .cipher_free = qcrypto_gcrypt_ctx_free, +}; + static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, @@ -259,6 +358,8 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, if (mode == QCRYPTO_CIPHER_MODE_CTR) { drv = &qcrypto_gcrypt_ctr_driver; + } else if (mode == QCRYPTO_CIPHER_MODE_GCM) { + drv = &qcrypto_gcrypt_gcm_driver; } else { drv = &qcrypto_gcrypt_driver; } From b0f7e8cc97d16133bfd7ce344b525188d68e30f1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:30 +0000 Subject: [PATCH 382/860] crypto/cipher-nettle: Implement AES-GCM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the AES-GCM AEAD mode to the nettle backend so it is available when QEMU is built with nettle instead of gcrypt. GCM is driven through nettle's generic gcm_* interface, using the AES encrypt function for both directions: gcm_set_iv() sets the (typically 96-bit) nonce, gcm_update() feeds the associated data, gcm_encrypt()/gcm_decrypt() need not be block aligned, and gcm_digest() produces the authentication tag. Signed-off-by: Jamin Lin Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- crypto/cipher-nettle.c.inc | 128 +++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/crypto/cipher-nettle.c.inc b/crypto/cipher-nettle.c.inc index 1afdc391b4..d4847f0efe 100644 --- a/crypto/cipher-nettle.c.inc +++ b/crypto/cipher-nettle.c.inc @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef CONFIG_CRYPTO_SM4 #include #endif @@ -410,6 +411,125 @@ DEFINE_ECB(qcrypto_nettle_sm4, sm4_encrypt_native, sm4_decrypt_native) #endif +/* + * GCM is an AEAD mode built on AES (128-bit block only). Drive it through the + * generic gcm_* interface, using the block cipher's encrypt function for both + * directions; associated data is fed with gcm_update() and the authentication + * tag is produced by gcm_digest(). + */ +typedef struct QCryptoNettleAESGCM { + QCryptoCipher base; + struct gcm_key gcm_key; + struct gcm_ctx gcm_ctx; + union { + struct aes128_ctx aes128; + struct aes192_ctx aes192; + struct aes256_ctx aes256; + } cipher; + nettle_cipher_func *encrypt; +} QCryptoNettleAESGCM; + +static int qcrypto_nettle_aes_gcm_setiv(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_set_iv(&ctx->gcm_ctx, &ctx->gcm_key, niv, iv); + return 0; +} + +static int qcrypto_nettle_aes_gcm_setaad(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_update(&ctx->gcm_ctx, &ctx->gcm_key, len, aad); + return 0; +} + +static int qcrypto_nettle_aes_gcm_encrypt(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_encrypt(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt, + len, out, in); + return 0; +} + +static int qcrypto_nettle_aes_gcm_decrypt(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_decrypt(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt, + len, out, in); + return 0; +} + +static int qcrypto_nettle_aes_gcm_gettag(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp) +{ + QCryptoNettleAESGCM *ctx = container_of(cipher, QCryptoNettleAESGCM, base); + + gcm_digest(&ctx->gcm_ctx, &ctx->gcm_key, &ctx->cipher, ctx->encrypt, + len, tag); + return 0; +} + +static const struct QCryptoCipherDriver qcrypto_nettle_aes_gcm_driver = { + .cipher_encrypt = qcrypto_nettle_aes_gcm_encrypt, + .cipher_decrypt = qcrypto_nettle_aes_gcm_decrypt, + .cipher_setiv = qcrypto_nettle_aes_gcm_setiv, + .cipher_setaad = qcrypto_nettle_aes_gcm_setaad, + .cipher_gettag = qcrypto_nettle_aes_gcm_gettag, + .cipher_free = qcrypto_cipher_ctx_free, +}; + +static QCryptoCipher *qcrypto_nettle_aes_gcm_ctx_new(QCryptoCipherAlgo alg, + const uint8_t *key, + size_t nkey, + Error **errp) +{ + QCryptoNettleAESGCM *ctx; + + if (!qcrypto_cipher_validate_key_length(alg, QCRYPTO_CIPHER_MODE_GCM, + nkey, errp)) { + return NULL; + } + + ctx = g_new0(QCryptoNettleAESGCM, 1); + ctx->base.driver = &qcrypto_nettle_aes_gcm_driver; + + switch (alg) { + case QCRYPTO_CIPHER_ALGO_AES_128: + aes128_set_encrypt_key(&ctx->cipher.aes128, key); + ctx->encrypt = aes128_encrypt_native; + break; + case QCRYPTO_CIPHER_ALGO_AES_192: + aes192_set_encrypt_key(&ctx->cipher.aes192, key); + ctx->encrypt = aes192_encrypt_native; + break; + case QCRYPTO_CIPHER_ALGO_AES_256: + aes256_set_encrypt_key(&ctx->cipher.aes256, key); + ctx->encrypt = aes256_encrypt_native; + break; + default: + error_setg(errp, "Unsupported cipher algorithm %s with GCM mode", + QCryptoCipherAlgo_str(alg)); + g_free(ctx); + return NULL; + } + + gcm_set_key(&ctx->gcm_key, &ctx->cipher, ctx->encrypt); + return &ctx->base; +} + bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, QCryptoCipherMode mode) { @@ -440,6 +560,10 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, case QCRYPTO_CIPHER_MODE_XTS: case QCRYPTO_CIPHER_MODE_CTR: return true; + case QCRYPTO_CIPHER_MODE_GCM: + return alg == QCRYPTO_CIPHER_ALGO_AES_128 || + alg == QCRYPTO_CIPHER_ALGO_AES_192 || + alg == QCRYPTO_CIPHER_ALGO_AES_256; default: return false; } @@ -451,6 +575,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, size_t nkey, Error **errp) { + if (mode == QCRYPTO_CIPHER_MODE_GCM) { + return qcrypto_nettle_aes_gcm_ctx_new(alg, key, nkey, errp); + } + switch (mode) { case QCRYPTO_CIPHER_MODE_ECB: case QCRYPTO_CIPHER_MODE_CBC: From 66c320fd5d492cc5287e4f5bde52def3efbd149e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:31 +0000 Subject: [PATCH 383/860] crypto/cipher-gnutls: Implement AES-GCM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the AES-GCM AEAD mode to the gnutls backend so it is available when QEMU is built with gnutls (neither gcrypt nor nettle). GCM uses the incremental gnutls_cipher_* API with the GNUTLS_CIPHER_AES_*_GCM algorithms: gnutls_cipher_set_iv() sets the nonce, gnutls_cipher_add_auth() feeds the associated data, gnutls_cipher_encrypt2()/decrypt2() process the message, and gnutls_cipher_tag() reads back the authentication tag. Signed-off-by: Jamin Lin Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- crypto/cipher-gnutls.c.inc | 154 +++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/crypto/cipher-gnutls.c.inc b/crypto/cipher-gnutls.c.inc index a8263fff6d..963b328fa1 100644 --- a/crypto/cipher-gnutls.c.inc +++ b/crypto/cipher-gnutls.c.inc @@ -48,6 +48,15 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, default: return false; } + case QCRYPTO_CIPHER_MODE_GCM: + switch (alg) { + case QCRYPTO_CIPHER_ALGO_AES_128: + case QCRYPTO_CIPHER_ALGO_AES_192: + case QCRYPTO_CIPHER_ALGO_AES_256: + return true; + default: + return false; + } default: return false; } @@ -223,6 +232,147 @@ static struct QCryptoCipherDriver gnutls_driver = { .cipher_free = qcrypto_gnutls_cipher_free, }; +/* + * GCM is an AEAD stream mode: the nonce need not match the block size, the + * message length need not be a multiple of the block size, associated data is + * fed with gnutls_cipher_add_auth() and the authentication tag is read back + * with gnutls_cipher_tag(). + */ +static int +qcrypto_gnutls_cipher_encrypt_gcm(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_encrypt2(ctx->handle, in, len, out, len); + if (err != 0) { + error_setg(errp, "Cannot encrypt data: %s", gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static int +qcrypto_gnutls_cipher_decrypt_gcm(QCryptoCipher *cipher, + const void *in, void *out, + size_t len, Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_decrypt2(ctx->handle, in, len, out, len); + if (err != 0) { + error_setg(errp, "Cannot decrypt data: %s", gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static int +qcrypto_gnutls_cipher_setiv_gcm(QCryptoCipher *cipher, + const uint8_t *iv, size_t niv, + Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + + gnutls_cipher_set_iv(ctx->handle, (void *)iv, niv); + + return 0; +} + +static int +qcrypto_gnutls_cipher_setaad_gcm(QCryptoCipher *cipher, + const uint8_t *aad, size_t len, + Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_add_auth(ctx->handle, aad, len); + if (err != 0) { + error_setg(errp, "Cannot add associated data: %s", + gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static int +qcrypto_gnutls_cipher_gettag_gcm(QCryptoCipher *cipher, + uint8_t *tag, size_t len, + Error **errp) +{ + QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base); + int err; + + err = gnutls_cipher_tag(ctx->handle, tag, len); + if (err != 0) { + error_setg(errp, "Cannot get authentication tag: %s", + gnutls_strerror(err)); + return -1; + } + + return 0; +} + +static struct QCryptoCipherDriver gnutls_gcm_driver = { + .cipher_encrypt = qcrypto_gnutls_cipher_encrypt_gcm, + .cipher_decrypt = qcrypto_gnutls_cipher_decrypt_gcm, + .cipher_setiv = qcrypto_gnutls_cipher_setiv_gcm, + .cipher_setaad = qcrypto_gnutls_cipher_setaad_gcm, + .cipher_gettag = qcrypto_gnutls_cipher_gettag_gcm, + .cipher_free = qcrypto_gnutls_cipher_free, +}; + +static QCryptoCipher * +qcrypto_gnutls_aes_gcm_ctx_new(QCryptoCipherAlgo alg, const uint8_t *key, + size_t nkey, Error **errp) +{ + gnutls_datum_t gkey = { (unsigned char *)key, nkey }; + gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN; + QCryptoCipherGnutls *ctx; + int err; + + switch (alg) { + case QCRYPTO_CIPHER_ALGO_AES_128: + galg = GNUTLS_CIPHER_AES_128_GCM; + break; + case QCRYPTO_CIPHER_ALGO_AES_192: + galg = GNUTLS_CIPHER_AES_192_GCM; + break; + case QCRYPTO_CIPHER_ALGO_AES_256: + galg = GNUTLS_CIPHER_AES_256_GCM; + break; + default: + error_setg(errp, "Unsupported cipher algorithm %s with GCM mode", + QCryptoCipherAlgo_str(alg)); + return NULL; + } + + if (!qcrypto_cipher_validate_key_length(alg, QCRYPTO_CIPHER_MODE_GCM, + nkey, errp)) { + return NULL; + } + + ctx = g_new0(QCryptoCipherGnutls, 1); + ctx->base.driver = &gnutls_gcm_driver; + ctx->blocksize = 16; + + err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL); + if (err != 0) { + error_setg(errp, "Cannot initialize cipher: %s", gnutls_strerror(err)); + g_free(ctx); + return NULL; + } + + return &ctx->base; +} + static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, QCryptoCipherMode mode, const uint8_t *key, @@ -234,6 +384,10 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN; int err; + if (mode == QCRYPTO_CIPHER_MODE_GCM) { + return qcrypto_gnutls_aes_gcm_ctx_new(alg, key, nkey, errp); + } + switch (mode) { case QCRYPTO_CIPHER_MODE_XTS: switch (alg) { From 69ddeb2fac7454daa6b5cd896cc91df96566ee25 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:32 +0000 Subject: [PATCH 384/860] tests/unit/test-crypto-cipher: Test AES-GCM mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exercise the new GCM mode and the setaad/gettag helpers with the canonical AES-GCM test vectors from the GCM specification (McGrew & Viega, also NIST SP 800-38D): AES-128 and AES-256, with and without associated data. Each vector is run through encrypt (checking the ciphertext and the generated tag) and decrypt (checking the recovered plaintext and the recomputed tag). Signed-off-by: Jamin Lin Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-13-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/unit/test-crypto-cipher.c | 240 ++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c index 1331d558cf..420c826df9 100644 --- a/tests/unit/test-crypto-cipher.c +++ b/tests/unit/test-crypto-cipher.c @@ -810,6 +810,230 @@ static void test_cipher_short_plaintext(void) qcrypto_cipher_free(cipher); } +typedef struct QCryptoCipherGcmTestData QCryptoCipherGcmTestData; +struct QCryptoCipherGcmTestData { + const char *path; + QCryptoCipherAlgo alg; + const char *key; + const char *iv; + /* associated data, or NULL for none */ + const char *aad; + const char *plaintext; + const char *ciphertext; + const char *tag; +}; + +/* + * AES-GCM test vectors from "The Galois/Counter Mode of Operation (GCM)" + * (McGrew & Viega, also NIST SP 800-38D), with a 96-bit IV and a 128-bit + * tag. Each entry's "Test case N" label is the numbered test case from that + * document (Appendix B / the GCM specification's test vectors). + */ +static QCryptoCipherGcmTestData gcm_test_data[] = { + { + /* Test case 2 */ + .path = "/crypto/cipher/aes-gcm-128-2", + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .key = "00000000000000000000000000000000", + .iv = "000000000000000000000000", + .plaintext = "00000000000000000000000000000000", + .ciphertext = "0388dace60b6a392f328c2b971b2fe78", + .tag = "ab6e47d42cec13bdf53a67b21257bddf", + }, + { + /* Test case 3 (no AAD) */ + .path = "/crypto/cipher/aes-gcm-128-3", + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .key = "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b391aafd255", + .ciphertext = + "42831ec2217774244b7221b784d0d49c" + "e3aa212f2c02a4e035c17e2329aca12e" + "21d514b25466931c7d8f6a5aac84aa05" + "1ba30b396a0aac973d58e091473f5985", + .tag = "4d5c2af327cd64a62cf35abd2ba6fab4", + }, + { + /* Test case 4 (with AAD) */ + .path = "/crypto/cipher/aes-gcm-128-4", + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .key = "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .aad = "feedfacedeadbeeffeedfacedeadbeefabaddad2", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b39", + .ciphertext = + "42831ec2217774244b7221b784d0d49c" + "e3aa212f2c02a4e035c17e2329aca12e" + "21d514b25466931c7d8f6a5aac84aa05" + "1ba30b396a0aac973d58e091", + .tag = "5bc94fbc3221a5db94fae95ae7121a47", + }, + { + /* Test case 15 (AES-256, no AAD) */ + .path = "/crypto/cipher/aes-gcm-256-15", + .alg = QCRYPTO_CIPHER_ALGO_AES_256, + .key = + "feffe9928665731c6d6a8f9467308308" + "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b391aafd255", + .ciphertext = + "522dc1f099567d07f47f37a32a84427d" + "643a8cdcbfe5c0c97598a2bd2555d1aa" + "8cb08e48590dbb3da7b08b1056828838" + "c5f61e6393ba7a0abcc9f662898015ad", + .tag = "b094dac5d93471bdec1a502270e3cc6c", + }, + { + /* Test case 16 (AES-256, with AAD) */ + .path = "/crypto/cipher/aes-gcm-256-16", + .alg = QCRYPTO_CIPHER_ALGO_AES_256, + .key = + "feffe9928665731c6d6a8f9467308308" + "feffe9928665731c6d6a8f9467308308", + .iv = "cafebabefacedbaddecaf888", + .aad = "feedfacedeadbeeffeedfacedeadbeefabaddad2", + .plaintext = + "d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b39", + .ciphertext = + "522dc1f099567d07f47f37a32a84427d" + "643a8cdcbfe5c0c97598a2bd2555d1aa" + "8cb08e48590dbb3da7b08b1056828838" + "c5f61e6393ba7a0abcc9f662", + .tag = "76fc6ece0f4e1768cddf8853bb2d551b", + }, +}; + +static void test_cipher_gcm(const void *opaque) +{ + const QCryptoCipherGcmTestData *data = opaque; + g_autofree uint8_t *key = NULL; + g_autofree uint8_t *iv = NULL; + g_autofree uint8_t *aad = NULL; + g_autofree uint8_t *ptext = NULL; + g_autofree uint8_t *ctext = NULL; + g_autofree uint8_t *tagexp = NULL; + g_autofree uint8_t *out = NULL; + uint8_t tag[16]; + size_t nkey; + size_t niv; + size_t naad = 0; + size_t nptext; + size_t nctext; + size_t ntag; + QCryptoCipher *cipher; + + nkey = unhex_string(data->key, &key); + niv = unhex_string(data->iv, &iv); + nptext = unhex_string(data->plaintext, &ptext); + nctext = unhex_string(data->ciphertext, &ctext); + ntag = unhex_string(data->tag, &tagexp); + if (data->aad) { + naad = unhex_string(data->aad, &aad); + } + + g_assert_cmpint(nptext, ==, nctext); + g_assert_cmpint(ntag, ==, sizeof(tag)); + out = g_new0(uint8_t, nptext); + + /* Encrypt: plaintext -> ciphertext, then read back the tag. */ + cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM, + key, nkey, &error_abort); + g_assert(cipher != NULL); + g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0); + if (naad) { + g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0); + } + g_assert(qcrypto_cipher_encrypt(cipher, ptext, out, nptext, + &error_abort) == 0); + g_assert_cmpmem(out, nptext, ctext, nctext); + g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag), + &error_abort) == 0); + g_assert_cmpmem(tag, sizeof(tag), tagexp, ntag); + qcrypto_cipher_free(cipher); + + /* Decrypt: ciphertext -> plaintext, recomputed tag must match. */ + memset(out, 0, nptext); + cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM, + key, nkey, &error_abort); + g_assert(cipher != NULL); + g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0); + if (naad) { + g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0); + } + g_assert(qcrypto_cipher_decrypt(cipher, ctext, out, nctext, + &error_abort) == 0); + g_assert_cmpmem(out, nctext, ptext, nptext); + g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag), + &error_abort) == 0); + g_assert_cmpmem(tag, sizeof(tag), tagexp, ntag); + qcrypto_cipher_free(cipher); +} + +/* + * Corrupt one ciphertext byte and confirm the recomputed GCM tag no longer + * matches: the authentication tag must detect tampering. + */ +static void test_cipher_gcm_tamper(const void *opaque) +{ + const QCryptoCipherGcmTestData *data = opaque; + g_autofree uint8_t *key = NULL; + g_autofree uint8_t *iv = NULL; + g_autofree uint8_t *aad = NULL; + g_autofree uint8_t *ctext = NULL; + g_autofree uint8_t *tagexp = NULL; + g_autofree uint8_t *out = NULL; + uint8_t tag[16]; + size_t nkey; + size_t niv; + size_t naad = 0; + size_t nctext; + size_t ntag; + QCryptoCipher *cipher; + + nkey = unhex_string(data->key, &key); + niv = unhex_string(data->iv, &iv); + nctext = unhex_string(data->ciphertext, &ctext); + ntag = unhex_string(data->tag, &tagexp); + if (data->aad) { + naad = unhex_string(data->aad, &aad); + } + out = g_new0(uint8_t, nctext); + + /* Flip one ciphertext bit before decrypting. */ + ctext[0] ^= 0x01; + + cipher = qcrypto_cipher_new(data->alg, QCRYPTO_CIPHER_MODE_GCM, + key, nkey, &error_abort); + g_assert(cipher != NULL); + g_assert(qcrypto_cipher_setiv(cipher, iv, niv, &error_abort) == 0); + if (naad) { + g_assert(qcrypto_cipher_setaad(cipher, aad, naad, &error_abort) == 0); + } + g_assert(qcrypto_cipher_decrypt(cipher, ctext, out, nctext, + &error_abort) == 0); + g_assert(qcrypto_cipher_gettag(cipher, tag, sizeof(tag), + &error_abort) == 0); + g_assert(memcmp(tag, tagexp, ntag) != 0); + qcrypto_cipher_free(cipher); +} + int main(int argc, char **argv) { size_t i; @@ -828,6 +1052,22 @@ int main(int argc, char **argv) } } + for (i = 0; i < G_N_ELEMENTS(gcm_test_data); i++) { + if (qcrypto_cipher_supports(gcm_test_data[i].alg, + QCRYPTO_CIPHER_MODE_GCM)) { + g_autofree char *tamper = g_strdup_printf("%s/tamper", + gcm_test_data[i].path); + + g_test_add_data_func(gcm_test_data[i].path, &gcm_test_data[i], + test_cipher_gcm); + g_test_add_data_func(tamper, &gcm_test_data[i], + test_cipher_gcm_tamper); + } else { + g_printerr("# skip unsupported %s:gcm\n", + QCryptoCipherAlgo_str(gcm_test_data[i].alg)); + } + } + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_256, QCRYPTO_CIPHER_MODE_CBC)) { g_test_add_func("/crypto/cipher/null-iv", From c98058bb3d3464be0923ffd5f88937ecc57a0cac Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:34 +0000 Subject: [PATCH 385/860] hw/misc/aspeed_hace: Support 64-bit DMA for the crypto command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2700 crypto engine addresses DRAM with 64 bits, supplying the high half of the source, destination and context addresses through HACE80, HACE84 and HACE88. Add those registers and a crypt_get_addr() helper that combines the low and high halves when the SoC has 64-bit DMA, mirroring the hash engine. SoCs without 64-bit DMA (AST2500/AST2600/AST1030) ignore the high registers, so their behaviour is unchanged. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-14-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index d84f7f9cec..0a6e2fa829 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -57,6 +57,11 @@ #define CRYPT_CTX_KEY_OFFSET 0x10 #define CRYPT_CTX_SIZE 0x30 +/* AST2700 64-bit DMA high address registers for the crypto command */ +#define R_CRYPT_SRC_HI (0x80 / 4) +#define R_CRYPT_DEST_HI (0x84 / 4) +#define R_CRYPT_CONTEXT_HI (0x88 / 4) + #define R_STATUS (0x1c / 4) #define HASH_IRQ BIT(9) #define CRYPT_IRQ BIT(12) @@ -670,6 +675,19 @@ static void crypt_be_add(uint8_t *ctr, size_t len, uint64_t add) } } +static uint64_t crypt_get_addr(AspeedHACEState *s, int reg, int reg_hi) +{ + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); + uint64_t addr; + + addr = deposit64(0, 0, 32, s->regs[reg]); + if (ahc->has_dma64) { + addr = deposit64(addr, 32, 32, s->regs[reg_hi]); + } + + return addr; +} + /* * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are * either single contiguous buffers (direct access mode) or scatter-gather @@ -717,7 +735,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) } /* Fetch the IV and key from the context buffer in DRAM. */ - ctx_addr = s->regs[R_CRYPT_CONTEXT]; + ctx_addr = crypt_get_addr(s, R_CRYPT_CONTEXT, R_CRYPT_CONTEXT_HI); if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED, ctx, sizeof(ctx))) { qemu_log_mask(LOG_GUEST_ERROR, @@ -758,7 +776,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) dst_buf = g_malloc0(buf_len); /* Gather the source into the bounce buffer, per the selected mode. */ - src_addr = s->regs[R_CRYPT_SRC]; + src_addr = crypt_get_addr(s, R_CRYPT_SRC, R_CRYPT_SRC_HI); if (sg_mode) { status = crypt_prepare_sg(s, src_addr, src_buf, len, false); } else { @@ -794,7 +812,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) } /* Scatter the result back out, per the selected mode. */ - dst_addr = s->regs[R_CRYPT_DEST]; + dst_addr = crypt_get_addr(s, R_CRYPT_DEST, R_CRYPT_DEST_HI); if (sg_mode) { status = crypt_prepare_sg(s, dst_addr, dst_buf, len, true); } else { @@ -958,6 +976,15 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, case R_HASH_KEY_BUFF_HI: data &= ahc->key_hi_mask; break; + case R_CRYPT_SRC_HI: + data &= ahc->src_hi_mask; + break; + case R_CRYPT_DEST_HI: + data &= ahc->dest_hi_mask; + break; + case R_CRYPT_CONTEXT_HI: + data &= ahc->key_hi_mask; + break; default: break; } From 7a1a61b7ace9a4f7dad5cc3d4d69459029360961 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:35 +0000 Subject: [PATCH 386/860] hw/misc/aspeed_hace: Support the AES-GCM mode for the crypto command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the AES-GCM mode (HACE10[6:4] = 0b101) used by the AST2700 crypto engine: decode the GCM selection, read the 96-bit IV from the context buffer, operate on the exact data length (GCM handles a partial final block itself), and write the 128-bit authentication tag to the tag buffer (HACE18/HACE8C). The hardware GCM path is only used without associated data (the driver falls back to software otherwise), so AAD is not modelled and a non-zero HACE14 is reported as unimplemented. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-15-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 70 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 0a6e2fa829..23dacc9be8 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -31,6 +31,9 @@ /* HACE0C[27:0] holds the crypto data length */ #define CRYPT_DATA_LEN_MASK 0x0FFFFFFF #define R_CRYPT_CMD (0x10 / 4) +/* AES-GCM associated data length (HACE14) and tag write buffer (HACE18) */ +#define R_CRYPT_GCM_ADD_LEN (0x14 / 4) +#define R_CRYPT_GCM_TAG (0x18 / 4) /* Crypto engine command register (HACE10) bits */ #define CRYPT_CMD_ENCRYPT BIT(7) #define CRYPT_CMD_ISR_EN BIT(12) @@ -42,6 +45,7 @@ #define CRYPT_CMD_ECB (0x0 << 4) #define CRYPT_CMD_CBC (0x1 << 4) #define CRYPT_CMD_CTR (0x4 << 4) +#define CRYPT_CMD_GCM (0x5 << 4) /* AES key length HACE10[3:2] */ #define CRYPT_CMD_AES_KEY_LEN_MASK (0x3 << 2) #define CRYPT_CMD_AES256 (0x2 << 2) @@ -57,10 +61,15 @@ #define CRYPT_CTX_KEY_OFFSET 0x10 #define CRYPT_CTX_SIZE 0x30 +/* AES-GCM uses a 96-bit IV and a 128-bit authentication tag */ +#define CRYPT_GCM_IV_LEN 12 +#define CRYPT_GCM_TAG_LEN 16 + /* AST2700 64-bit DMA high address registers for the crypto command */ #define R_CRYPT_SRC_HI (0x80 / 4) #define R_CRYPT_DEST_HI (0x84 / 4) #define R_CRYPT_CONTEXT_HI (0x88 / 4) +#define R_CRYPT_GCM_TAG_HI (0x8c / 4) #define R_STATUS (0x1c / 4) #define HASH_IRQ BIT(9) @@ -596,6 +605,9 @@ static bool crypt_decode_cmd(uint32_t cmd, QCryptoCipherAlgo *alg, case CRYPT_CMD_CTR: *mode = QCRYPTO_CIPHER_MODE_CTR; break; + case CRYPT_CMD_GCM: + *mode = QCRYPTO_CIPHER_MODE_GCM; + break; default: return false; } @@ -689,11 +701,12 @@ static uint64_t crypt_get_addr(AspeedHACEState *s, int reg, int reg_hi) } /* - * Perform an AES/DES/3DES ECB/CBC operation. The source and destination are - * either single contiguous buffers (direct access mode) or scatter-gather - * lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the IV/key come from - * the context buffer (HACE08). For CBC the resulting chaining IV is written - * back to the context buffer so the driver can continue the chain. + * Perform an AES/DES/3DES ECB/CBC/CTR or AES-GCM operation. The source and + * destination are either single contiguous buffers (direct access mode) or + * scatter-gather lists (HACE10[18]/[19]), addressed by HACE00/HACE04; the + * IV/key come from the context buffer (HACE08). For CBC and CTR the resulting + * chaining state is written back to the context buffer so the driver can + * continue; for GCM the authentication tag is written to the tag buffer. */ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) { @@ -703,6 +716,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) g_autoptr(QCryptoCipher) cipher = NULL; g_autofree uint8_t *src_buf = NULL; g_autofree uint8_t *dst_buf = NULL; + uint8_t tag[CRYPT_GCM_TAG_LEN]; uint8_t ctx[CRYPT_CTX_SIZE]; Error *local_err = NULL; QCryptoCipherMode mode; @@ -711,10 +725,13 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) uint64_t ctx_addr; uint64_t src_addr; uint64_t dst_addr; + uint64_t tag_addr; + uint32_t aad_len; size_t iv_offset; size_t blocklen; size_t buf_len; size_t keylen; + size_t ivlen; bool status; if (len == 0) { @@ -734,6 +751,20 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) return; } + /* GCM uses a 96-bit IV; the block modes use a full-block IV. */ + ivlen = (mode == QCRYPTO_CIPHER_MODE_GCM) ? CRYPT_GCM_IV_LEN : blocklen; + + /* + * The hardware GCM path is only exercised without associated data (the + * driver falls back to software when there is any), so AAD is not modelled. + */ + aad_len = s->regs[R_CRYPT_GCM_ADD_LEN]; + if (mode == QCRYPTO_CIPHER_MODE_GCM && aad_len != 0) { + qemu_log_mask(LOG_UNIMP, + "%s: GCM associated data is not implemented\n", __func__); + return; + } + /* Fetch the IV and key from the context buffer in DRAM. */ ctx_addr = crypt_get_addr(s, R_CRYPT_CONTEXT, R_CRYPT_CONTEXT_HI); if (address_space_read(&s->dram_as, ctx_addr, MEMTXATTRS_UNSPECIFIED, @@ -758,7 +789,7 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) } if (mode != QCRYPTO_CIPHER_MODE_ECB && - qcrypto_cipher_setiv(cipher, ctx + iv_offset, blocklen, + qcrypto_cipher_setiv(cipher, ctx + iv_offset, ivlen, &local_err) < 0) { qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher setiv failed: %s\n", __func__, error_get_pretty(local_err)); @@ -769,9 +800,11 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) /* * Round the working buffers up to a whole block. Block modes are already * block-aligned; the stream-like CTR mode may leave a partial final block - * that the engine still processes a full block at a time. + * that the engine still processes a full block at a time. GCM handles a + * partial final block itself, so it operates on the exact length. */ - buf_len = QEMU_ALIGN_UP(len, blocklen); + buf_len = (mode == QCRYPTO_CIPHER_MODE_GCM) ? + len : QEMU_ALIGN_UP(len, blocklen); src_buf = g_malloc0(buf_len); dst_buf = g_malloc0(buf_len); @@ -855,6 +888,24 @@ static void do_crypt_operation(AspeedHACEState *s, uint32_t cmd) "%s: Failed to write IV, addr=0x%" HWADDR_PRIx "\n", __func__, ctx_addr + iv_offset); } + } else if (mode == QCRYPTO_CIPHER_MODE_GCM) { + /* + * GCM authenticates the message and writes the resulting tag to the + * dedicated tag buffer (HACE18/HACE8C). + */ + if (qcrypto_cipher_gettag(cipher, tag, sizeof(tag), &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto cipher gettag failed: " + "%s\n", __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + tag_addr = crypt_get_addr(s, R_CRYPT_GCM_TAG, R_CRYPT_GCM_TAG_HI); + if (address_space_write(&s->dram_as, tag_addr, MEMTXATTRS_UNSPECIFIED, + tag, sizeof(tag))) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write tag, addr=0x%" HWADDR_PRIx "\n", + __func__, tag_addr); + } } } @@ -899,9 +950,11 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, case R_CRYPT_SRC: case R_CRYPT_DEST: case R_CRYPT_CONTEXT: + case R_CRYPT_GCM_TAG: data &= ahc->src_mask; break; case R_CRYPT_DATA_LEN: + case R_CRYPT_GCM_ADD_LEN: data &= CRYPT_DATA_LEN_MASK; break; case R_HASH_SRC: @@ -980,6 +1033,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, data &= ahc->src_hi_mask; break; case R_CRYPT_DEST_HI: + case R_CRYPT_GCM_TAG_HI: data &= ahc->dest_hi_mask; break; case R_CRYPT_CONTEXT_HI: From baf15a2cf21c75ec9c3f068991cf28844887bf74 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:36 +0000 Subject: [PATCH 387/860] hw/misc/aspeed_hace: Enable the crypto command on the AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With direct/scatter-gather access, 64-bit DMA and AES-GCM all in place, the AST2700 crypto engine is now fully modelled. Drop its temporary interrupt-only workaround so the crypto command runs for real, like the other HACE variants. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-16-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 15 +-------------- include/hw/misc/aspeed_hace.h | 1 - 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 23dacc9be8..8de05a9d98 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -1005,14 +1005,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, break; } case R_CRYPT_CMD: - /* - * The AST2700 crypto engine needs 64-bit DMA and AES-GCM, which are - * added later; until then it keeps the temporary workaround of only - * raising the completion interrupt without running the command. - */ - if (!ahc->raise_crypt_interrupt_workaround) { - do_crypt_operation(s, data); - } + do_crypt_operation(s, data); /* Hardware raises the crypt interrupt once the command finishes. */ s->regs[R_STATUS] |= CRYPT_IRQ; @@ -1214,12 +1207,6 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data) ahc->dest_hi_mask = 0x00000003; ahc->key_hi_mask = 0x00000003; - /* - * Currently, it does not support the CRYPT command. Instead, it only - * sends an interrupt to notify the firmware that the crypt command - * has completed. It is a temporary workaround. - */ - ahc->raise_crypt_interrupt_workaround = true; ahc->has_dma64 = true; } diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index b5416b0cb5..9b0e7683fa 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -49,7 +49,6 @@ struct AspeedHACEClass { uint32_t key_mask; uint32_t hash_mask; uint64_t nr_regs; - bool raise_crypt_interrupt_workaround; uint32_t src_hi_mask; uint32_t dest_hi_mask; uint32_t key_hi_mask; From 0375f6498eee6188089924bb766c653492ff8857 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 11 Aug 2026 06:01:37 +0000 Subject: [PATCH 388/860] tests/qtest/aspeed-hace: Test the crypto command on the AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the AST2700 crypto engine, which drives 64-bit scatter-gather DMA and adds AES-GCM on top of the ECB/CBC/CTR modes shared with the AST2600. Add AES-128 and AES-256 GCM known-answer vectors (GCM specification / NIST SP 800-38D, no associated data) and a dedicated GCM runner that programs the tag buffer and reads the tag back, checking it after both encryption and decryption. Register the AST2700 with all four modes. Signed-off-by: Jamin Lin Reviewed-by: Kane Chen Link: https://lore.kernel.org/qemu-devel/20260811060115.1849266-17-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 163 +++++++++++++++++++++++++++++++- tests/qtest/aspeed-hace-utils.h | 1 + tests/qtest/ast2700-hace-test.c | 9 ++ 3 files changed, 172 insertions(+), 1 deletion(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index f582c88ef5..260eec043c 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -654,6 +654,8 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, #define HACE_CRYPTO_CONTEXT 0x08 #define HACE_CRYPTO_DATA_LEN 0x0c #define HACE_CRYPTO_CMD 0x10 +#define HACE_CRYPTO_GCM_ADD_LEN 0x14 +#define HACE_CRYPTO_GCM_TAG 0x18 /* Crypto command bits */ #define HACE_CMD_ENCRYPT BIT(7) @@ -666,7 +668,9 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, #define HACE_CMD_ECB (0x0 << 4) #define HACE_CMD_CBC (0x1 << 4) #define HACE_CMD_CTR (0x4 << 4) +#define HACE_CMD_GCM (0x5 << 4) #define HACE_CMD_AES128 (0x0 << 2) +#define HACE_CMD_AES256 (0x2 << 2) /* Context buffer layout: IV (DES at +8), key at +0x10 */ #define HACE_CTX_KEY_OFFSET 0x10 @@ -794,6 +798,59 @@ static const uint8_t tdes_ctr_ctext[8] = { static const uint8_t tdes_ctr_ivout[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +/* + * aes_gcm_tv_template[2] (AES-128) and [9] (AES-256), from the McGrew & Viega + * GCM spec (also NIST SP 800-38D), no AAD. Both cases share this plaintext/IV. + */ +static const uint8_t aes_gcm_ptext[64] = { + 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, + 0xba, 0x63, 0x7b, 0x39, 0x1a, 0xaf, 0xd2, 0x55 }; +static const uint8_t aes_gcm_iv[12] = { + 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, + 0xde, 0xca, 0xf8, 0x88 }; + +/* aes_gcm_tv_template[2] (AES-128) */ +static const uint8_t aes128_gcm_key[16] = { + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }; +static const uint8_t aes128_gcm_ctext[64] = { + 0x42, 0x83, 0x1e, 0xc2, 0x21, 0x77, 0x74, 0x24, + 0x4b, 0x72, 0x21, 0xb7, 0x84, 0xd0, 0xd4, 0x9c, + 0xe3, 0xaa, 0x21, 0x2f, 0x2c, 0x02, 0xa4, 0xe0, + 0x35, 0xc1, 0x7e, 0x23, 0x29, 0xac, 0xa1, 0x2e, + 0x21, 0xd5, 0x14, 0xb2, 0x54, 0x66, 0x93, 0x1c, + 0x7d, 0x8f, 0x6a, 0x5a, 0xac, 0x84, 0xaa, 0x05, + 0x1b, 0xa3, 0x0b, 0x39, 0x6a, 0x0a, 0xac, 0x97, + 0x3d, 0x58, 0xe0, 0x91, 0x47, 0x3f, 0x59, 0x85 }; +static const uint8_t aes128_gcm_tag[16] = { + 0x4d, 0x5c, 0x2a, 0xf3, 0x27, 0xcd, 0x64, 0xa6, + 0x2c, 0xf3, 0x5a, 0xbd, 0x2b, 0xa6, 0xfa, 0xb4 }; + +/* aes_gcm_tv_template[9] (AES-256) */ +static const uint8_t aes256_gcm_key[32] = { + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08 }; +static const uint8_t aes256_gcm_ctext[64] = { + 0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07, + 0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d, + 0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9, + 0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa, + 0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d, + 0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a, + 0xbc, 0xc9, 0xf6, 0x62, 0x89, 0x80, 0x15, 0xad }; +static const uint8_t aes256_gcm_tag[16] = { + 0xb0, 0x94, 0xda, 0xc5, 0xd9, 0x34, 0x71, 0xbd, + 0xec, 0x1a, 0x50, 0x22, 0x70, 0xe3, 0xcc, 0x6c }; + typedef struct CryptTest { QCryptoCipherMode mode; QCryptoCipherAlgo alg; @@ -801,10 +858,13 @@ typedef struct CryptTest { const uint8_t *iv_out; const uint8_t *ptext; const uint8_t *ctext; + /* expected GCM authentication tag, or NULL for non-AEAD modes */ + const uint8_t *tag; const uint8_t *key; const uint8_t *iv; const char *name; size_t keylen; + size_t taglen; /* algorithm | mode | key size selection */ uint32_t cmd; size_t ivlen; @@ -927,6 +987,36 @@ static const CryptTest crypt_tests[] = { .iv_out = tdes_ctr_ivout, .len = sizeof(tdes_ctr_ptext), }, + { + .name = "aes128-gcm", + .cmd = HACE_CMD_AES128 | HACE_CMD_GCM, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, + .mode = QCRYPTO_CIPHER_MODE_GCM, + .key = aes128_gcm_key, + .keylen = sizeof(aes128_gcm_key), + .iv = aes_gcm_iv, + .ivlen = sizeof(aes_gcm_iv), + .ptext = aes_gcm_ptext, + .ctext = aes128_gcm_ctext, + .tag = aes128_gcm_tag, + .taglen = sizeof(aes128_gcm_tag), + .len = sizeof(aes_gcm_ptext), + }, + { + .name = "aes256-gcm", + .cmd = HACE_CMD_AES256 | HACE_CMD_GCM, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, + .mode = QCRYPTO_CIPHER_MODE_GCM, + .key = aes256_gcm_key, + .keylen = sizeof(aes256_gcm_key), + .iv = aes_gcm_iv, + .ivlen = sizeof(aes_gcm_iv), + .ptext = aes_gcm_ptext, + .ctext = aes256_gcm_ctext, + .tag = aes256_gcm_tag, + .taglen = sizeof(aes256_gcm_tag), + .len = sizeof(aes_gcm_ptext), + }, }; /* DRAM offsets for the crypto test source, destination and context buffers. */ @@ -944,6 +1034,8 @@ static const CryptTest crypt_tests[] = { */ #define CRYPT_SG_FRAGS 3 #define CRYPT_SG_FRAG_STRIDE 0x1000 +/* DRAM offset for the AES-GCM authentication tag write buffer. */ +#define CRYPT_OFF_TAG 0x60000 /* Describes one registered crypto test (qtest_add_data_func() data pointer). */ typedef struct AspeedCryptoTest { @@ -964,6 +1056,8 @@ static uint32_t crypt_mode_flag(uint32_t cmd) return CRYPT_MODE_CBC; case HACE_CMD_CTR: return CRYPT_MODE_CTR; + case HACE_CMD_GCM: + return CRYPT_MODE_GCM; default: return 0; } @@ -1109,6 +1203,47 @@ static void crypt_run_sg(QTestState *s, uint32_t base, uint64_t dram, crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len); } +/* + * Run one AES-GCM operation in scatter-gather mode: like crypt_run_sg() but + * also program the tag write buffer (HACE18) with no associated data, and read + * the authentication tag back into @out_tag. + */ +static void crypt_run_gcm(QTestState *s, uint32_t base, uint64_t dram, + const CryptTest *t, bool encrypt, uint8_t *out, + uint8_t *out_tag) +{ + const uint8_t *in = encrypt ? t->ptext : t->ctext; + uint64_t src_sg = dram + CRYPT_OFF_SRC_SG; + uint64_t dst_sg = dram + CRYPT_OFF_DST_SG; + uint64_t ctx = dram + CRYPT_OFF_CTX; + uint32_t cmd = t->cmd | HACE_CMD_ISR_EN | HACE_CMD_SRC_SG_CTRL | + HACE_CMD_DST_SG_CTRL; + + if (encrypt) { + cmd |= HACE_CMD_ENCRYPT; + } + + crypt_write_ctx(s, ctx, t); + crypt_make_sg(s, dram, CRYPT_OFF_SRC, src_sg, in, t->len); + crypt_make_sg(s, dram, CRYPT_OFF_DST, dst_sg, NULL, t->len); + + qtest_writel(s, base + HACE_CRYPTO_SRC, (uint32_t)src_sg); + qtest_writel(s, base + HACE_CRYPTO_DEST, (uint32_t)dst_sg); + qtest_writel(s, base + HACE_CRYPTO_CONTEXT, (uint32_t)ctx); + qtest_writel(s, base + HACE_CRYPTO_DATA_LEN, t->len); + qtest_writel(s, base + HACE_CRYPTO_GCM_ADD_LEN, 0); + qtest_writel(s, base + HACE_CRYPTO_GCM_TAG, + (uint32_t)(dram + CRYPT_OFF_TAG)); + qtest_writel(s, base + HACE_CRYPTO_CMD, cmd); + + g_assert_cmphex(qtest_readl(s, base + HACE_STS) & HACE_CRYPTO_ISR, ==, + HACE_CRYPTO_ISR); + qtest_writel(s, base + HACE_STS, HACE_CRYPTO_ISR); + + crypt_gather_sg(s, dram, CRYPT_OFF_DST, out, t->len); + qtest_memread(s, dram + CRYPT_OFF_TAG, out_tag, t->taglen); +} + static void aspeed_test_crypto(const void *data) { const AspeedCryptoTest *c = data; @@ -1145,6 +1280,29 @@ static void aspeed_test_crypto(const void *data) qtest_quit(s); } +static void aspeed_test_crypto_gcm(const void *data) +{ + const AspeedCryptoTest *c = data; + const CryptTest *t = &crypt_tests[c->index]; + QTestState *s = qtest_init(c->machine); + uint8_t out[64]; + uint8_t tag[16]; + + g_assert_cmpuint(t->len, <=, sizeof(out)); + + /* Encrypt: ptext -> ctext, then check the authentication tag. */ + crypt_run_gcm(s, c->base, c->dram, t, true, out, tag); + g_assert_cmpmem(out, t->len, t->ctext, t->len); + g_assert_cmpmem(tag, t->taglen, t->tag, t->taglen); + + /* Decrypt: ctext -> ptext, the recomputed tag must match. */ + crypt_run_gcm(s, c->base, c->dram, t, false, out, tag); + g_assert_cmpmem(out, t->len, t->ptext, t->len); + g_assert_cmpmem(tag, t->taglen, t->tag, t->taglen); + + qtest_quit(s); +} + void aspeed_add_crypto_tests(const char *prefix, const char *machine, uint32_t base, uint64_t dram, uint32_t modes, bool sg) @@ -1152,6 +1310,7 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine, int i; for (i = 0; i < ARRAY_SIZE(crypt_tests); i++) { + bool is_gcm = crypt_tests[i].mode == QCRYPTO_CIPHER_MODE_GCM; g_autofree char *path = NULL; AspeedCryptoTest *t; @@ -1173,7 +1332,9 @@ void aspeed_add_crypto_tests(const char *prefix, const char *machine, t->dram = dram; t->index = i; t->sg = sg; - qtest_add_data_func_full(path, t, aspeed_test_crypto, g_free); + qtest_add_data_func_full(path, t, + is_gcm ? aspeed_test_crypto_gcm : + aspeed_test_crypto, g_free); } } diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index 82b0b3f93d..a5601a3d65 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -86,6 +86,7 @@ enum { CRYPT_MODE_ECB = 1 << 0, CRYPT_MODE_CBC = 1 << 1, CRYPT_MODE_CTR = 1 << 2, + CRYPT_MODE_GCM = 1 << 3, }; /* diff --git a/tests/qtest/ast2700-hace-test.c b/tests/qtest/ast2700-hace-test.c index 508a34dd6c..3f0217d635 100644 --- a/tests/qtest/ast2700-hace-test.c +++ b/tests/qtest/ast2700-hace-test.c @@ -94,5 +94,14 @@ int main(int argc, char **argv) qtest_add_func("ast2700/hace/sha384_accum", test_sha384_accum_ast2700); qtest_add_func("ast2700/hace/sha256_accum", test_sha256_accum_ast2700); + /* + * The AST2700 crypto engine uses scatter-gather with 64-bit DMA and adds + * AES-GCM on top of the ECB/CBC/CTR modes shared with the AST2600. + */ + aspeed_add_crypto_tests("ast2700", "-machine ast2700-evb", 0x12070000, + 0x400000000, + CRYPT_MODE_ECB | CRYPT_MODE_CBC | CRYPT_MODE_CTR | + CRYPT_MODE_GCM, true); + return g_test_run(); } From 8b6405a493a2106eaa3893237595afb4759ca498 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 21 Jul 2026 10:14:57 -0300 Subject: [PATCH 389/860] docs: Add security considerations for migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the security considerations that are unique to migration and that do not already fall into one of the other categories. Some aspects are better framed as security architecture considerations, so extend that section to mention TLS and clarify that disk images and guest network also need to be isolated from other processes, not just other guests. Reviewed-by: Peter Xu Reviewed-by: Daniel P. Berrangé Signed-off-by: Fabiano Rosas Link: https://lore.kernel.org/r/20260721131457.3062767-1-farosas@suse.de Signed-off-by: Peter Xu --- docs/system/security.rst | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/system/security.rst b/docs/system/security.rst index 52bbf0cc7a..af626a4230 100644 --- a/docs/system/security.rst +++ b/docs/system/security.rst @@ -133,6 +133,16 @@ an issue as a normal bug. that affect the level 0 QEMU process. While these bugs should be fixed, they will not be triaged as security flaws at this time. +* **migration/snapshots**. Migration failures and snapshot load + failures are considered part of normal operation as long as the + source virtual machine and savevm file, respectively, are still + functional. Aborting the QEMU process at the migration/snapshot + destination is similarly not considered a security issue. The + migration stream is assumed to be secure as long as the design + principles described in the Architecture section are held, in + which case plain manipulation of the stream is not considered as + an attack vector. + * **low severity impact**. As a catch all rule, issues which are judged to have a "low" severity impact on the system will usually not justify handling as security bugs, nor assignment @@ -159,10 +169,11 @@ could allow malicious guests to gain code execution in QEMU. At this point the guest has escaped the virtual machine and is able to act in the context of the QEMU process on the host. -Guests often interact with other guests and share resources with them. A -malicious guest must not gain control of other guests or access their data. -Disk image files and network traffic must be protected from other guests unless -explicitly shared between them by the user. +Guests often interact with other guests and share resources with them. +A malicious guest must not gain control of other guests or access +their data. Disk image files and network traffic must be protected +from other guests, users and processes unless explicitly shared with +them by the user. Principle of Least Privilege '''''''''''''''''''''''''''' @@ -223,6 +234,9 @@ Some Linux distros already ship with UNIX groups for these devices by default. system calls that are not needed by QEMU, thereby reducing the host kernel attack surface. +- Transport Layer Security (TLS) protocol can be used to ensure authenticity and + encryption of the live migration connection where the network is untrusted. + Sensitive configurations ------------------------ From 403ccbc55d7bfdef41998fbe26967b017663c3eb Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Tue, 28 Jul 2026 01:59:03 -0700 Subject: [PATCH 390/860] migration/cpr: Add HMP support for cpr-transfer Currently the cpr-transfer source QEMU instance cannot be driven entirely via HMP. The source must use QMP in order to specify both the main migration channel and the CPR channel. Extend the HMP migrate command with an optional CPR channel URI. When the migration mode is cpr-transfer, HMP uses this URI to build a CPR MigrationChannel in addition to the main migration channel. The new option is rejected unless the migration mode is cpr-transfer, so existing HMP migrate usage is unchanged. For example, source QEMU HMP commands can be something like below. The "-c unix:/tmp/cpr.sock" is for CPR URI. (qemu) migrate_set_parameter mode cpr-transfer (qemu) migrate -c unix:/tmp/cpr.sock tcp:0:50002 Signed-off-by: Dongli Zhang Reviewed-by: Dr. David Alan Gilbert Acked-by: Maciej S. Szmigiero Link: https://lore.kernel.org/r/20260728085903.173265-1-dongli.zhang@oracle.com Signed-off-by: Peter Xu --- hmp-commands.hx | 12 ++++++++---- migration/migration-hmp-cmds.c | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 7ae2468a3d..7f43cf537f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -928,16 +928,17 @@ ERST { .name = "migrate", - .args_type = "detach:-d,resume:-r,uri:s", - .params = "[-d] [-r] uri", + .args_type = "detach:-d,resume:-r,uri-cpr:-cs,uri:s", + .params = "[-d] [-r] [-c uri-cpr] uri", .help = "migrate to URI (using -d to not wait for completion)" - "\n\t\t\t -r to resume a paused postcopy migration", + "\n\t\t\t -r to resume a paused postcopy migration" + "\n\t\t\t -c to specify a CPR URI for cpr-transfer mode", .cmd = hmp_migrate, }, SRST -``migrate [-d] [-r]`` *uri* +``migrate [-d] [-r] [-c uri-cpr]`` *uri* Migrate the VM to *uri*. ``-d`` @@ -945,6 +946,9 @@ SRST query an ongoing migration process, use "info migrate". ``-r`` Resume a paused postcopy migration. + ``-c`` *uri-cpr* + Specify the CPR URI for cpr-transfer mode. It must be a UNIX domain + socket. ERST { diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index b04fc4489f..b5eb27467c 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -837,9 +837,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) bool detach = qdict_get_try_bool(qdict, "detach", false); bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); + const char *uri_cpr = qdict_get_try_str(qdict, "uri-cpr"); Error *err = NULL; g_autoptr(MigrationChannelList) caps = NULL; g_autoptr(MigrationChannel) channel = NULL; + g_autoptr(MigrationChannel) channel_cpr = NULL; if (!migrate_uri_parse(uri, &channel, &err)) { hmp_handle_error(mon, err); @@ -847,6 +849,22 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) } QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); + if (uri_cpr) { + if (migrate_mode() != MIG_MODE_CPR_TRANSFER) { + error_setg(&err, "-c can only be used in cpr-transfer mode"); + hmp_handle_error(mon, err); + return; + } + + if (!migrate_uri_parse(uri_cpr, &channel_cpr, &err)) { + hmp_handle_error(mon, err); + return; + } + + channel_cpr->channel_type = MIGRATION_CHANNEL_TYPE_CPR; + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel_cpr)); + } + qmp_migrate(NULL, true, caps, true, resume, &err); if (hmp_handle_error(mon, err)) { return; From d2d7e63da8e9a6ac4bb23e3ecc1de468530c9437 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 28 Jul 2026 13:17:29 +1000 Subject: [PATCH 391/860] system/memory: Use memmove() for directly accessible regions Similar to what's done in commit 4a73aee88140 ("softmmu: Use memmove in flatview_write_continue"), there are more sites where the overlapping source and destination buffer are allowed for the directly accessible regions. Use memmove() in those sites, listed as below. hw/remote/vfio-user-obj.c::vfu_object_mr_rw include/system/memory.h::address_space_read system/physmem.c::flatview_read_continue_step Signed-off-by: Gavin Shan Reviewed-by: Peter Maydell Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20260728031731.286666-2-gshan@redhat.com Signed-off-by: Peter Xu --- hw/remote/vfio-user-obj.c | 4 ++-- include/system/memory.h | 2 +- system/physmem.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 87fa7b6572..ea50270628 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -375,9 +375,9 @@ static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset, ram_ptr = memory_region_get_ram_ptr(mr); if (is_write) { - memcpy((ram_ptr + offset), buf, size); + memmove((ram_ptr + offset), buf, size); } else { - memcpy(buf, (ram_ptr + offset), size); + memmove(buf, (ram_ptr + offset), size); } return 0; diff --git a/include/system/memory.h b/include/system/memory.h index 2192fc9bdc..336d4e84a6 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -2741,7 +2741,7 @@ MemTxResult address_space_read(const AddressSpace *as, hwaddr addr, mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); if (len == l && memory_access_is_direct(mr, false, attrs)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); - memcpy(buf, ptr, len); + memmove(buf, ptr, len); } else { result = flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); diff --git a/system/physmem.c b/system/physmem.c index c21ea92915..2c42e365cb 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3363,7 +3363,7 @@ static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf, uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, false, false); - memcpy(buf, ram_ptr, *l); + memmove(buf, ram_ptr, *l); return MEMTX_OK; } From bec5e7c71cc9770a900dac6930d076fe7ee9e57b Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 28 Jul 2026 13:17:30 +1000 Subject: [PATCH 392/860] system/memory: Use qemu_ram_move() for directly accessible regions All ram device regions were turned to be indirectly accessible by commit 4a2e242bbb ("memory: Don't use memcpy for ram_device regions"). This leads to guest hang on attempt to build 'cuda-samples' as reported by Julia. The guest is started by the following command lines, with GH100 GPU card passed from the host. host$ lspci | grep GH100 0009:01:00.0 3D controller: NVIDIA Corporation GH100 [GH200 120GB / 480GB] (rev a1) host$ /home/sandbox/gavin/qemu.main/build/qemu-system-aarch64 \ -machine virt,gic-version=host,ras=on,highmem-mmio-size=4T \ -accel kvm -cpu host -smp cpus=48 -m size=8G \ -drive file=/home/gavin/sandbox/images/disk.qcow2,if=none,id=d0 \ -device virtio-blk-pci,id=vb0,bus=pcie.0,drive=d0,num-queues=4 \ -device vfio-pci-nohotplug,host=0009:01:00.0,bus=pcie.1.0 : guest$ cd cuda-samples/build guest$ make -j 20 clean guest$ make -j 20 : [ 54%] Linking CUDA executable graphMemoryNodes [ 54%] Built target graphMemoryNodes guest$ qemu-system-aarch64: virtio: bogus descriptor or out of resources [ 555.814025] virtio_blk virtio0: [vda] new size: 268435456 512-byte logical blocks (137 GB/128 GiB) When the GPU's driver (NVidia open driver) is loaded on guest bootup, the memory blocks residing in the PCI BAR#4 of the GH100 GPU card can be presented to the guest through memory hot-add. The page cache can then be allocated from the hot added memory blocks when cuda-samples is being built. Afterwards, the page cache is sent to QEMU's virtio-blk device as part of the DMA request, the bounce buffer has to be used to accomodate the request as the corresponding memory region (MemoryRegion) is an indirectly accessible ram device region in qemu. However, the max bounce bufer size is only 4096 bytes by default and that is exhausted quickly, leading to a reset on the virtio-blk device and frozen guest eventually. QEMU ==== virtio_blk_handle_output virtio_blk_handle_vq virtio_blk_get_request virtqueue_pop virtqueue_split_pop virtqueue_map_desc address_space_map memory_access_is_direct # Return false memory_region_supports_direct_access (qemu) info mtree memory-region: pci_bridge_pci 0000000000000000-ffffffffffffffff (prio 0, container): pci_bridge_pci 0000042000000000-0000043fffffffff (prio 1, i/o): 0009:01:00.0 base BAR 4 0000042000000000-0000043fffffffff (prio 0, i/o): 0009:01:00.0 BAR 4 0000042000000000-000004379fffffff (prio 0, ramd): 0009:01:00.0 BAR 4 mmaps[0] This adds qemu_ram_move() where the aligned and small-sized accesses are handled by qatomics, and fall back to memmove() otherwise. The memove() for the directly accessible regions is replaced by qemu_ram_move() so that the issue covered by commit 4a2e242bbb (MMIO access instructions were optimized to SSE instructions) is fixed. This makes 'ram_device_mem_ops' redundant, paving the way to revert that commit to make the ram device region directly accessible again in the next patch. Besides, this also fixes the issue of the unexpected frozen reception on e1000 NIC in the scenario of DPDK due to the wrong Rx queue full indication caused by the following memcpy(), which is turned to 3 consective 'strb' instructions to the same location by glibc-2.24+ for aarch64. With this applied, the syntax of one-byte store is strictly ensured by a one-byte qatomic set. QEMU ==== e1000_receive_iov pci_dma_write pci_dma_rw dma_memory_rw dma_memory_rw_relaxed address_space_rw address_space_write flatview_write flatview_write_continue flatview_write_continue_step memcpy # 3 consective 'strb' instructions Reported-by: Julia Graham Reported-by: Liu Gang Reported-by: Ding Hui Suggested-by: Michael S. Tsirkin Suggested-by: Peter Xu Suggested-by: Richard Henderson Suggested-by: Peter Maydell Signed-off-by: Gavin Shan Reviewed-by: Peter Maydell Link: https://lore.kernel.org/r/20260728031731.286666-3-gshan@redhat.com [peterx: remove src==dst check, fix doc, enhance comments, per PeterM, add R-b] Signed-off-by: Peter Xu --- hw/remote/vfio-user-obj.c | 4 ++-- include/system/memory.h | 35 +++++++++++++++++++++++++++- system/physmem.c | 48 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index ea50270628..a0498d218f 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -375,9 +375,9 @@ static int vfu_object_mr_rw(MemoryRegion *mr, uint8_t *buf, hwaddr offset, ram_ptr = memory_region_get_ram_ptr(mr); if (is_write) { - memmove((ram_ptr + offset), buf, size); + qemu_ram_move((ram_ptr + offset), buf, size); } else { - memmove(buf, (ram_ptr + offset), size); + qemu_ram_move(buf, (ram_ptr + offset), size); } return 0; diff --git a/include/system/memory.h b/include/system/memory.h index 336d4e84a6..4de5bf2577 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -2668,6 +2668,39 @@ void address_space_register_map_client(AddressSpace *as, QEMUBH *bh); void address_space_unregister_map_client(AddressSpace *as, QEMUBH *bh); /* Internal functions, part of the implementation of address_space_read. */ + +/** + * qemu_ram_move: move data from or to ramblock + * + * @dst: destination where the data is moved to + * @src: source where the data is moved from + * @n: length of data to be moved + * + * Move @n bytes from @src to @dst, the memory areas may overlap. This + * provides the same semantics as memmove(), plus an additional stronger + * guarantee: if @n is 1, 2 or 4 or 8 bytes, and @src and @dst are both + * naturally aligned for that access size, then both the load and the store + * will be done as a single atomic access (with the semantics of + * qatomic_read() and qatomic_set()). + * + * This is the underlying function that we use to implement accesses by + * a guest vCPU or a device DMA operation to a ram block. The atomic + * guarantee is needed for two major cases: (A) When the ram block is + * backed by a PCI BAR passed through from a host device (and so it might + * be hardware registers that must be accessed exactly once at the right + * width); (B) When an emulated device updates a data structure shared in + * guest memory with guest software (e.g. a network device's set of tx and + * rx descriptor blocks), if a write to memory is accidentally performed + * multiple times then it can break the guest code when it busy polls the + * guest memory. + * + * We don't attempt to perform the exact access when it would be unaligned + * because this can't be done on all host architectures. Although this is + * strictly speaking not doing what would happen on real hardware, we don't + * think there are going to be situations where that matters in practice. + */ +void qemu_ram_move(void *dst, const void *src, size_t n); + MemTxResult address_space_read_full(const AddressSpace *as, hwaddr addr, MemTxAttrs attrs, void *buf, hwaddr len); MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, @@ -2741,7 +2774,7 @@ MemTxResult address_space_read(const AddressSpace *as, hwaddr addr, mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); if (len == l && memory_access_is_direct(mr, false, attrs)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); - memmove(buf, ptr, len); + qemu_ram_move(buf, ptr, len); } else { result = flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); diff --git a/system/physmem.c b/system/physmem.c index 2c42e365cb..2f37cbeb07 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3158,6 +3158,50 @@ void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size) invalidate_and_set_dirty(mr, addr, size); } +void qemu_ram_move(void *dst, const void *src, size_t n) +{ + uintptr_t test, len; + + if (n == 0) { + return; + } + + /* + * Calculate "the lowest set bit" over @src, @dst and @n, result put + * into @len (which guarantees a power-of-two). With that and the + * later check (len!=n), it makes sure that we will only do the atomic + * ops when: + * + * (1) @n is a power-of-two + * (2) @src and @dst addresses are both aligned to @n + */ + test = (uintptr_t)src | (uintptr_t)dst | n; + len = test & -test; + + /* Overlapping buffers, unaligned or oversized access */ + if (n > 8 || len != n) { + memmove(dst, src, n); + return; + } + + switch (len) { + case 1: + qatomic_set((uint8_t *)dst, qatomic_read((uint8_t *)src)); + break; + case 2: + qatomic_set((uint16_t *)dst, qatomic_read((uint16_t *)src)); + break; + case 4: + qatomic_set((uint32_t *)dst, qatomic_read((uint32_t *)src)); + break; + case 8: + qatomic_set((uint64_t *)dst, qatomic_read((uint64_t *)src)); + break; + default: + g_assert_not_reached(); + } +} + int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) { unsigned access_size_max = mr->ops->valid.max_access_size; @@ -3270,7 +3314,7 @@ static MemTxResult flatview_write_continue_step(MemTxAttrs attrs, uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, false, true); - memmove(ram_ptr, buf, *l); + qemu_ram_move(ram_ptr, buf, *l); invalidate_and_set_dirty(mr, mr_addr, *l); return MEMTX_OK; @@ -3363,7 +3407,7 @@ static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf, uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l, false, false); - memmove(buf, ram_ptr, *l); + qemu_ram_move(buf, ram_ptr, *l); return MEMTX_OK; } From ff77a515113f052cc5fc571b6a328142fdfcf220 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Tue, 28 Jul 2026 13:17:31 +1000 Subject: [PATCH 393/860] system/memory: Make ram device region directly accessible This basically reverts 4a2e242bbb30 ("memory: Don't use memcpy for ram_device regions") to make ram device region directly accessible again. With this, the bounce buffer is bypassed in address_space_map() when a ram device region is involved, potentially avoid to overrun the (small) bounce buffer. Reported-by: Julia Graham Suggested-by: Michael S. Tsirkin Suggested-by: Peter Xu Suggested-by: Richard Henderson Suggested-by: Peter Maydell Signed-off-by: Gavin Shan Reviewed-by: Peter Maydell Link: https://lore.kernel.org/r/20260728031731.286666-4-gshan@redhat.com Signed-off-by: Peter Xu --- include/system/memory.h | 11 ++--------- system/memory.c | 41 +---------------------------------------- system/trace-events | 2 -- 3 files changed, 3 insertions(+), 51 deletions(-) diff --git a/include/system/memory.h b/include/system/memory.h index 4de5bf2577..1dc761058f 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -2718,15 +2718,8 @@ static inline bool memory_region_supports_direct_access(const MemoryRegion *mr) if (memory_region_is_romd(mr)) { return true; } - if (!memory_region_is_ram(mr)) { - return false; - } - /* - * RAM DEVICE regions can be accessed directly using memcpy, but it might - * be MMIO and access using mempy can be wrong (e.g., using instructions not - * intended for MMIO access). So we treat this as IO. - */ - return !memory_region_is_ram_device(mr); + + return memory_region_is_ram(mr); } static inline bool memory_access_is_direct(const MemoryRegion *mr, diff --git a/system/memory.c b/system/memory.c index 5fc36708ec..da710bbade 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1364,43 +1364,6 @@ const MemoryRegionOps unassigned_mem_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static uint64_t memory_region_ram_device_read(void *opaque, - hwaddr addr, unsigned size) -{ - MemoryRegion *mr = opaque; - uint64_t data = ldn_he_p(mr->ram_block->host + addr, size); - - trace_memory_region_ram_device_read(get_cpu_index(), mr, addr, data, size); - - return data; -} - -static void memory_region_ram_device_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - MemoryRegion *mr = opaque; - - trace_memory_region_ram_device_write(get_cpu_index(), mr, addr, data, size); - - stn_he_p(mr->ram_block->host + addr, size, data); -} - -static const MemoryRegionOps ram_device_mem_ops = { - .read = memory_region_ram_device_read, - .write = memory_region_ram_device_write, - .endianness = HOST_BIG_ENDIAN ? DEVICE_BIG_ENDIAN : DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 8, - .unaligned = true, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 8, - .unaligned = true, - }, -}; - bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, unsigned size, @@ -1692,10 +1655,8 @@ void memory_region_init_ram_device_ptr(MemoryRegion *mr, Object *owner, const char *name, uint64_t size, void *ptr) { - memory_region_init_io(mr, owner, &ram_device_mem_ops, mr, name, size); - mr->ram = true; + memory_region_init_ram_ptr(mr, owner, name, size, ptr); mr->ram_device = true; - memory_region_set_ram_ptr(mr, size, ptr); } void memory_region_init_alias(MemoryRegion *mr, Object *owner, diff --git a/system/trace-events b/system/trace-events index 51b4a4679a..d483b31419 100644 --- a/system/trace-events +++ b/system/trace-events @@ -20,8 +20,6 @@ memory_region_ops_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, u memory_region_ops_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size, const char *name) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u name '%s'" memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" -memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" memory_region_sync_dirty(const char *mr, const char *listener, int global) "mr '%s' listener '%s' synced (global=%d)" flatview_new(void *view, void *root) "%p (root %p)" flatview_destroy(void *view, void *root) "%p (root %p)" From bdfe26faca7b7c8daa7895783d55ec6d05164880 Mon Sep 17 00:00:00 2001 From: Sam Heney Date: Thu, 30 Jul 2026 23:59:44 +0100 Subject: [PATCH 394/860] tests/qtest/migration: Only build tls_no_hostname test with TASN1 The test_precopy_tcp_tls_no_hostname test and its start hook use TestMigrateTLSX509 and migrate_hook_start_tls_x509_common(), which are only defined when CONFIG_TASN1 is set. This means building with gnutls enabled but libtasn1 unavailable fails: ../tests/qtest/migration/tls-tests.c: In function 'migrate_hook_start_tls_x509_no_host': ../tests/qtest/migration/tls-tests.c:510:5: error: unknown type name 'TestMigrateTLSX509' Guard the test with CONFIG_TASN1 like the other x509 tests. Fixes: df9c38b19af8 ("tests/qtest/migration: Add a NULL parameters test for TLS") Signed-off-by: Sam Heney Link: https://lore.kernel.org/r/5f24de0e-49af-45a7-927f-f79b203bb335@app.fastmail.com Signed-off-by: Peter Xu --- tests/qtest/migration/tls-tests.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index 827cc7bcf8..9bdb1165af 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -492,6 +492,7 @@ static void test_precopy_tcp_no_tls(char *name, MigrateCommon *args) test_precopy_common(args); } +#ifdef CONFIG_TASN1 static void * migrate_hook_start_tls_x509_no_host(QTestState *from, QTestState *to) { @@ -519,7 +520,6 @@ static void test_precopy_tcp_tls_no_hostname(char *name, MigrateCommon *args) test_precopy_common(args); } -#ifdef CONFIG_TASN1 static void test_precopy_tcp_tls_x509_default_host(char *name, MigrateCommon *args) { @@ -719,8 +719,10 @@ void migration_test_add_tls(MigrationTestEnv *env) migration_test_add("/migration/precopy/tcp/no-tls", test_precopy_tcp_no_tls); +#ifdef CONFIG_TASN1 migration_test_add("/migration/precopy/tcp/tls/no-hostname", test_precopy_tcp_tls_no_hostname); +#endif /* CONFIG_TASN1 */ migration_test_add("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); From e8d693e12af9cbb89d724baadfcc08559669e279 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 11 Aug 2026 14:31:44 -0400 Subject: [PATCH 395/860] tests/qtest: Do not use versioned pc-q35-5.2 machine anymore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of QEMU v11.1.0, the v5.2.0 machines are not usable anymore. Use the latest x86 q35 machine instead, otherwise we get: $ qemu-system-x86_64 -M pc-q35-5.2 qemu-system-x86_64: unsupported machine type: "pc-q35-5.2" Use -machine help to list supported machines See commit a35f8577a07 ("include/hw: add macros for deprecation & removal of versioned machines") and f59ee044067 ("include/hw/boards: cope with dev/rc versions in deprecation checks") for explanation on automatically removed versioned machines. This commit message is taken from commit 9eef3854d309 ("tests/qtest: Do not use versioned pc-q35-5.0 machine anymore") by Philippe Mathieu-Daudé . Cc: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Peter Maydell Message-ID: <20260811183144.190135-1-stefanha@redhat.com> Signed-off-by: Stefan Hajnoczi --- tests/qtest/fuzz-virtio-scsi-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/fuzz-virtio-scsi-test.c b/tests/qtest/fuzz-virtio-scsi-test.c index e37b48b2cc..102a5ccb67 100644 --- a/tests/qtest/fuzz-virtio-scsi-test.c +++ b/tests/qtest/fuzz-virtio-scsi-test.c @@ -19,7 +19,7 @@ static void test_mmio_oob_from_memory_region_cache(void) { QTestState *s; - s = qtest_init("-M pc-q35-5.2 -m 512M " + s = qtest_init("-M q35 -m 512M " "-device virtio-scsi,num_queues=8,addr=03.0 "); qtest_outl(s, 0xcf8, 0x80001811); From 197870349f2b6e5bb3ed89bf1a949ecaca09cd78 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:06 -0700 Subject: [PATCH 396/860] hw/hexagon: add hex-subsys The virt and DSP machine models build the same core subsystem, let's abstract out that part. Start with the DDR and config table ROM setup. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-2-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/hex-subsys.c | 32 ++++++++++++++++++++++++++++++++ hw/hexagon/hexagon_dsp.c | 17 ++--------------- hw/hexagon/meson.build | 1 + hw/hexagon/virt.c | 14 ++------------ include/hw/hexagon/hex-subsys.h | 17 +++++++++++++++++ 5 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 hw/hexagon/hex-subsys.c create mode 100644 include/hw/hexagon/hex-subsys.h diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c new file mode 100644 index 0000000000..d146ee0c07 --- /dev/null +++ b/hw/hexagon/hex-subsys.c @@ -0,0 +1,32 @@ +/* + * Hexagon subsystem helpers shared between the machine models. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hexagon/hex-subsys.h" +#include "hw/core/loader.h" +#include "system/address-spaces.h" + +void hex_subsys_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + MachineState *machine = MACHINE(hms); + MemoryRegion *sysmem = get_system_memory(); + + /* Main DDR at the reset vector. */ + memory_region_init_ram(&hms->ram, NULL, "ddr.ram", machine->ram_size, + &error_fatal); + memory_region_add_subregion(sysmem, 0x0, &hms->ram); + + /* Config-table ROM and the blob that backs it. */ + memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", + sizeof(m_cfg->cfgtable), &error_fatal); + memory_region_add_subregion(sysmem, m_cfg->cfgbase, &hms->cfgtable_rom); + rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, + sizeof(m_cfg->cfgtable), m_cfg->cfgbase, + &address_space_memory); +} diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c index aa49399322..f94f7fd4e1 100644 --- a/hw/hexagon/hexagon_dsp.c +++ b/hw/hexagon/hexagon_dsp.c @@ -14,6 +14,7 @@ #include "hw/core/boards.h" #include "hw/core/qdev-properties.h" #include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hex-subsys.h" #include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" #include "hw/core/loader.h" @@ -108,7 +109,6 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, { HexagonCommonMachineState *hms = HEXAGON_COMMON_MACHINE(machine); HexagonDspMachineState *dms = HEXAGON_DSP_MACHINE(machine); - MemoryRegion *address_space; DeviceState *glob_regs_dev; DeviceState *tlb_dev; @@ -120,16 +120,7 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, machine->enable_graphics = 0; - address_space = get_system_memory(); - - memory_region_init_rom(&hms->cfgtable_rom, NULL, "config_table.rom", - sizeof(m_cfg->cfgtable), &error_fatal); - memory_region_add_subregion(address_space, m_cfg->cfgbase, - &hms->cfgtable_rom); - - memory_region_init_ram(&hms->ram, NULL, "ddr.ram", - machine->ram_size, &error_fatal); - memory_region_add_subregion(address_space, 0x0, &hms->ram); + hex_subsys_create(hms, m_cfg); glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); object_property_add_child(OBJECT(machine), "global-regs", @@ -162,10 +153,6 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, OBJECT(tlb_dev), &error_fatal); qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); } - - rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, - sizeof(m_cfg->cfgtable), m_cfg->cfgbase, - &address_space_memory); } static void init_mc(MachineClass *mc) diff --git a/hw/hexagon/meson.build b/hw/hexagon/meson.build index bade3a3292..720a5d54dc 100644 --- a/hw/hexagon/meson.build +++ b/hw/hexagon/meson.build @@ -1,6 +1,7 @@ hexagon_ss = ss.source_set() hexagon_ss.add(files('hexagon_tlb.c')) hexagon_ss.add(files('hexagon_globalreg.c')) +hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hex-subsys.c')) hexagon_ss.add(when: 'CONFIG_HEX_DSP', if_true: files('hexagon_dsp.c')) hexagon_ss.add(when: 'CONFIG_HEX_VIRT', if_true: files('virt.c')) diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index b750472502..ad0bc4f132 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -13,6 +13,7 @@ #include "hw/core/clock.h" #include "hw/core/sysbus-fdt.h" #include "hw/hexagon/hexagon.h" +#include "hw/hexagon/hex-subsys.h" #include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" #include "hw/core/loader.h" @@ -244,9 +245,7 @@ static void virt_init(MachineState *ms) vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk"); clock_set_hz(vms->apb_clk, 24000000); - memory_region_init_ram(&vms->parent_obj.ram, NULL, "ddr.ram", - ms->ram_size, &error_fatal); - memory_region_add_subregion(vms->sys, 0x0, &vms->parent_obj.ram); + hex_subsys_create(&vms->parent_obj, m_cfg); if (m_cfg->l2tcm_size) { memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size, @@ -255,11 +254,6 @@ static void virt_init(MachineState *ms) &vms->tcm); } - memory_region_init_rom(&vms->parent_obj.cfgtable_rom, NULL, - "config_table.rom", sizeof(m_cfg->cfgtable), - &error_fatal); - memory_region_add_subregion(vms->sys, m_cfg->cfgbase, - &vms->parent_obj.cfgtable_rom); fdt_add_hvx(vms, m_cfg); gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); @@ -302,10 +296,6 @@ static void virt_init(MachineState *ms) clk_phandle = fdt_add_clocks(vms); fdt_add_uart(vms, VIRT_UART0, clk_phandle); - rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, - sizeof(m_cfg->cfgtable), m_cfg->cfgbase, - &address_space_memory); - hexagon_load_fdt(vms); } diff --git a/include/hw/hexagon/hex-subsys.h b/include/hw/hexagon/hex-subsys.h new file mode 100644 index 0000000000..6bcde303f2 --- /dev/null +++ b/include/hw/hexagon/hex-subsys.h @@ -0,0 +1,17 @@ +/* + * Hexagon subsystem helpers shared between the machine models. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_HEXAGON_HEX_SUBSYS_H +#define HW_HEXAGON_HEX_SUBSYS_H + +#include "hw/hexagon/hexagon.h" + +/* Create the subsystem shared by every Hexagon machine. */ +void hex_subsys_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg); + +#endif /* HW_HEXAGON_HEX_SUBSYS_H */ From 024fff8b8160e20621731502213e1a077567b839 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:07 -0700 Subject: [PATCH 397/860] hw/hexagon: move VTCM to the common machine state The VTCM is described by the config table, so every machine can set it up the same way. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-3-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/hex-subsys.c | 8 ++++++++ hw/hexagon/virt.c | 5 ----- include/hw/hexagon/hexagon.h | 1 + include/hw/hexagon/virt.h | 1 - 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index d146ee0c07..0c20cf02e7 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -29,4 +29,12 @@ void hex_subsys_create(HexagonCommonMachineState *hms, rom_add_blob_fixed_as("config_table.rom", &m_cfg->cfgtable, sizeof(m_cfg->cfgtable), m_cfg->cfgbase, &address_space_memory); + + if (m_cfg->cfgtable.vtcm_size_kb > 0) { + memory_region_init_ram(&hms->vtcm, NULL, "vtcm.ram", + m_cfg->cfgtable.vtcm_size_kb * 1024, + &error_fatal); + memory_region_add_subregion(sysmem, m_cfg->cfgtable.vtcm_base << 16, + &hms->vtcm); + } } diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index ad0bc4f132..53eaafaf85 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -75,11 +75,6 @@ static void fdt_add_hvx(HexagonVirtMachineState *vms, const MachineState *ms = MACHINE(vms); uint32_t vtcm_size_bytes = m_cfg->cfgtable.vtcm_size_kb * 1024; if (vtcm_size_bytes > 0) { - memory_region_init_ram(&vms->vtcm, NULL, "vtcm.ram", vtcm_size_bytes, - &error_fatal); - memory_region_add_subregion(vms->sys, m_cfg->cfgtable.vtcm_base << 16, - &vms->vtcm); - qemu_fdt_add_subnode(ms->fdt, "/soc/vtcm"); qemu_fdt_setprop_string(ms->fdt, "/soc/vtcm", "compatible", "qcom,hexagon_vtcm"); diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index 1034b09c2a..9c73cadb16 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -156,6 +156,7 @@ struct HexagonCommonMachineState { MemoryRegion ram; MemoryRegion cfgtable_rom; + MemoryRegion vtcm; }; #endif diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h index fcb4776219..cf0cd2f8f2 100644 --- a/include/hw/hexagon/virt.h +++ b/include/hw/hexagon/virt.h @@ -17,7 +17,6 @@ struct HexagonVirtMachineState { int fdt_size; MemoryRegion *sys; MemoryRegion tcm; - MemoryRegion vtcm; MemoryRegion bios; Clock *apb_clk; }; From 26088c1e7b39e05a724618518594383665b74069 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:08 -0700 Subject: [PATCH 398/860] hw/hexagon: move global registers to hex-subsys Both machines create the global register device the same way, so let hex-subsys own it and link it to each CPU as it is realized. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-4-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/hex-subsys.c | 29 ++++++++++++++++++++++++++++- hw/hexagon/hexagon_dsp.c | 15 ++------------- hw/hexagon/virt.c | 23 +++++------------------ include/hw/hexagon/hex-subsys.h | 6 +++++- include/hw/hexagon/hexagon.h | 1 + 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index 0c20cf02e7..6789b6bb23 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -8,11 +8,29 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hexagon/hex-subsys.h" +#include "hw/hexagon/hexagon_globalreg.h" #include "hw/core/loader.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/qdev.h" +#include "hw/core/sysbus.h" #include "system/address-spaces.h" +static DeviceState *globalreg_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg, + Rev_t rev) +{ + DeviceState *glob_regs = qdev_new(TYPE_HEXAGON_GLOBALREG); + + object_property_add_child(OBJECT(hms), "global-regs", OBJECT(glob_regs)); + qdev_prop_set_uint64(glob_regs, "config-table-addr", m_cfg->cfgbase); + qdev_prop_set_uint32(glob_regs, "dsp-rev", rev); + sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs), &error_fatal); + + return glob_regs; +} + void hex_subsys_create(HexagonCommonMachineState *hms, - const struct hexagon_machine_config *m_cfg) + const struct hexagon_machine_config *m_cfg, Rev_t rev) { MachineState *machine = MACHINE(hms); MemoryRegion *sysmem = get_system_memory(); @@ -37,4 +55,13 @@ void hex_subsys_create(HexagonCommonMachineState *hms, memory_region_add_subregion(sysmem, m_cfg->cfgtable.vtcm_base << 16, &hms->vtcm); } + + hms->glob_regs = globalreg_create(hms, m_cfg, rev); +} + +void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) +{ + object_property_set_link(OBJECT(cpu), "global-regs", + OBJECT(hms->glob_regs), &error_fatal); + qdev_realize_and_unref(cpu, NULL, &error_fatal); } diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c index f94f7fd4e1..8599f5883b 100644 --- a/hw/hexagon/hexagon_dsp.c +++ b/hw/hexagon/hexagon_dsp.c @@ -15,7 +15,6 @@ #include "hw/core/qdev-properties.h" #include "hw/hexagon/hexagon.h" #include "hw/hexagon/hex-subsys.h" -#include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" #include "hw/core/loader.h" #include "qapi/error.h" @@ -109,7 +108,6 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, { HexagonCommonMachineState *hms = HEXAGON_COMMON_MACHINE(machine); HexagonDspMachineState *dms = HEXAGON_DSP_MACHINE(machine); - DeviceState *glob_regs_dev; DeviceState *tlb_dev; memset(&hexagon_binfo, 0, sizeof(hexagon_binfo)); @@ -120,14 +118,7 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, machine->enable_graphics = 0; - hex_subsys_create(hms, m_cfg); - - glob_regs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); - object_property_add_child(OBJECT(machine), "global-regs", - OBJECT(glob_regs_dev)); - qdev_prop_set_uint64(glob_regs_dev, "config-table-addr", m_cfg->cfgbase); - qdev_prop_set_uint32(glob_regs_dev, "dsp-rev", rev); - sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs_dev), &error_fatal); + hex_subsys_create(hms, m_cfg, rev); tlb_dev = qdev_new(TYPE_HEXAGON_TLB); object_property_add_child(OBJECT(machine), "tlb", OBJECT(tlb_dev)); @@ -147,11 +138,9 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, if (i == 0) { hexagon_init_bootstrap(dms, cpu); } - object_property_set_link(OBJECT(cpu), "global-regs", - OBJECT(glob_regs_dev), &error_fatal); object_property_set_link(OBJECT(cpu), "tlb", OBJECT(tlb_dev), &error_fatal); - qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + hex_subsys_realize_cpu(hms, DEVICE(cpu)); } } diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index 53eaafaf85..290783d511 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -14,7 +14,6 @@ #include "hw/core/sysbus-fdt.h" #include "hw/hexagon/hexagon.h" #include "hw/hexagon/hex-subsys.h" -#include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" @@ -226,9 +225,7 @@ static void virt_init(MachineState *ms) { HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms); const struct hexagon_machine_config *m_cfg = &v68n_1024; - DeviceState *gsregs_dev; DeviceState *tlb_dev; - DeviceState *cpu0; int32_t clk_phandle; create_fdt(vms); @@ -240,7 +237,7 @@ static void virt_init(MachineState *ms) vms->apb_clk = clock_new(OBJECT(ms), "apb-pclk"); clock_set_hz(vms->apb_clk, 24000000); - hex_subsys_create(&vms->parent_obj, m_cfg); + hex_subsys_create(&vms->parent_obj, m_cfg, v68_rev); if (m_cfg->l2tcm_size) { memory_region_init_ram(&vms->tcm, NULL, "tcm.ram", m_cfg->l2tcm_size, @@ -251,42 +248,32 @@ static void virt_init(MachineState *ms) fdt_add_hvx(vms, m_cfg); - gsregs_dev = qdev_new(TYPE_HEXAGON_GLOBALREG); - object_property_add_child(OBJECT(ms), "global-regs", OBJECT(gsregs_dev)); - qdev_prop_set_uint64(gsregs_dev, "config-table-addr", m_cfg->cfgbase); - qdev_prop_set_uint32(gsregs_dev, "dsp-rev", v68_rev); - sysbus_realize_and_unref(SYS_BUS_DEVICE(gsregs_dev), &error_fatal); - tlb_dev = qdev_new(TYPE_HEXAGON_TLB); object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev)); qdev_prop_set_uint32(tlb_dev, "num-entries", m_cfg->cfgtable.jtlb_size_entries); sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); - cpu0 = NULL; for (int i = 0; i < ms->smp.cpus; i++) { HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type)); qemu_register_reset(do_cpu_reset, cpu); if (i == 0) { - cpu0 = DEVICE(cpu); if (ms->kernel_filename) { uint64_t entry = load_kernel(vms); - qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", entry); } else if (ms->firmware) { uint64_t entry = load_bios(vms); - qdev_prop_set_uint32(cpu0, "exec-start-addr", entry); + qdev_prop_set_uint32(DEVICE(cpu), "exec-start-addr", entry); } } qdev_prop_set_uint32(DEVICE(cpu), "htid", i); qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); - object_property_set_link(OBJECT(cpu), "global-regs", - OBJECT(gsregs_dev), &error_fatal); object_property_set_link(OBJECT(cpu), "tlb", OBJECT(tlb_dev), &error_fatal); - - qdev_realize_and_unref(DEVICE(cpu), NULL, &error_fatal); + hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpu)); } + fdt_add_cpu_nodes(vms); clk_phandle = fdt_add_clocks(vms); fdt_add_uart(vms, VIRT_UART0, clk_phandle); diff --git a/include/hw/hexagon/hex-subsys.h b/include/hw/hexagon/hex-subsys.h index 6bcde303f2..087c105cfa 100644 --- a/include/hw/hexagon/hex-subsys.h +++ b/include/hw/hexagon/hex-subsys.h @@ -9,9 +9,13 @@ #define HW_HEXAGON_HEX_SUBSYS_H #include "hw/hexagon/hexagon.h" +#include "hw/core/qdev.h" /* Create the subsystem shared by every Hexagon machine. */ void hex_subsys_create(HexagonCommonMachineState *hms, - const struct hexagon_machine_config *m_cfg); + const struct hexagon_machine_config *m_cfg, Rev_t rev); + +/* Realize a CPU into the subsystem. */ +void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu); #endif /* HW_HEXAGON_HEX_SUBSYS_H */ diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index 9c73cadb16..ef5700082c 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -157,6 +157,7 @@ struct HexagonCommonMachineState { MemoryRegion ram; MemoryRegion cfgtable_rom; MemoryRegion vtcm; + DeviceState *glob_regs; }; #endif From d24f9c80f62f95cb8f6c2b8666d3efd1e021fb99 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:09 -0700 Subject: [PATCH 399/860] hw/hexagon: move the TLB to hex-subsys The TLB device is sized from the config table, so both machines create it identically. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-5-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/hex-subsys.c | 16 ++++++++++++++++ hw/hexagon/hexagon_dsp.c | 10 ---------- hw/hexagon/virt.c | 10 ---------- include/hw/hexagon/hexagon.h | 1 + 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index 6789b6bb23..ac7e29604e 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -9,6 +9,7 @@ #include "qapi/error.h" #include "hw/hexagon/hex-subsys.h" #include "hw/hexagon/hexagon_globalreg.h" +#include "hw/hexagon/hexagon_tlb.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" #include "hw/core/qdev.h" @@ -29,6 +30,18 @@ static DeviceState *globalreg_create(HexagonCommonMachineState *hms, return glob_regs; } +static DeviceState *tlb_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + DeviceState *tlb = qdev_new(TYPE_HEXAGON_TLB); + + object_property_add_child(OBJECT(hms), "tlb", OBJECT(tlb)); + qdev_prop_set_uint32(tlb, "num-entries", m_cfg->cfgtable.jtlb_size_entries); + sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb), &error_fatal); + + return tlb; +} + void hex_subsys_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg, Rev_t rev) { @@ -57,11 +70,14 @@ void hex_subsys_create(HexagonCommonMachineState *hms, } hms->glob_regs = globalreg_create(hms, m_cfg, rev); + hms->tlb = tlb_create(hms, m_cfg); } void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) { object_property_set_link(OBJECT(cpu), "global-regs", OBJECT(hms->glob_regs), &error_fatal); + object_property_set_link(OBJECT(cpu), "tlb", OBJECT(hms->tlb), + &error_fatal); qdev_realize_and_unref(cpu, NULL, &error_fatal); } diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c index 8599f5883b..25e8550fba 100644 --- a/hw/hexagon/hexagon_dsp.c +++ b/hw/hexagon/hexagon_dsp.c @@ -15,7 +15,6 @@ #include "hw/core/qdev-properties.h" #include "hw/hexagon/hexagon.h" #include "hw/hexagon/hex-subsys.h" -#include "hw/hexagon/hexagon_tlb.h" #include "hw/core/loader.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -108,7 +107,6 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, { HexagonCommonMachineState *hms = HEXAGON_COMMON_MACHINE(machine); HexagonDspMachineState *dms = HEXAGON_DSP_MACHINE(machine); - DeviceState *tlb_dev; memset(&hexagon_binfo, 0, sizeof(hexagon_binfo)); if (machine->kernel_filename) { @@ -120,12 +118,6 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, hex_subsys_create(hms, m_cfg, rev); - tlb_dev = qdev_new(TYPE_HEXAGON_TLB); - object_property_add_child(OBJECT(machine), "tlb", OBJECT(tlb_dev)); - qdev_prop_set_uint32(tlb_dev, "num-entries", - m_cfg->cfgtable.jtlb_size_entries); - sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); - for (int i = 0; i < machine->smp.cpus; i++) { HexagonCPU *cpu = HEXAGON_CPU(object_new(machine->cpu_type)); qemu_register_reset(do_cpu_reset, cpu); @@ -138,8 +130,6 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, if (i == 0) { hexagon_init_bootstrap(dms, cpu); } - object_property_set_link(OBJECT(cpu), "tlb", - OBJECT(tlb_dev), &error_fatal); hex_subsys_realize_cpu(hms, DEVICE(cpu)); } } diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index 290783d511..b9e0bbb704 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -14,7 +14,6 @@ #include "hw/core/sysbus-fdt.h" #include "hw/hexagon/hexagon.h" #include "hw/hexagon/hex-subsys.h" -#include "hw/hexagon/hexagon_tlb.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" #include "hw/core/qdev-clock.h" @@ -225,7 +224,6 @@ static void virt_init(MachineState *ms) { HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms); const struct hexagon_machine_config *m_cfg = &v68n_1024; - DeviceState *tlb_dev; int32_t clk_phandle; create_fdt(vms); @@ -248,12 +246,6 @@ static void virt_init(MachineState *ms) fdt_add_hvx(vms, m_cfg); - tlb_dev = qdev_new(TYPE_HEXAGON_TLB); - object_property_add_child(OBJECT(ms), "tlb", OBJECT(tlb_dev)); - qdev_prop_set_uint32(tlb_dev, "num-entries", - m_cfg->cfgtable.jtlb_size_entries); - sysbus_realize_and_unref(SYS_BUS_DEVICE(tlb_dev), &error_fatal); - for (int i = 0; i < ms->smp.cpus; i++) { HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type)); qemu_register_reset(do_cpu_reset, cpu); @@ -269,8 +261,6 @@ static void virt_init(MachineState *ms) } qdev_prop_set_uint32(DEVICE(cpu), "htid", i); qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); - object_property_set_link(OBJECT(cpu), "tlb", - OBJECT(tlb_dev), &error_fatal); hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpu)); } diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index ef5700082c..6edd93f478 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -158,6 +158,7 @@ struct HexagonCommonMachineState { MemoryRegion cfgtable_rom; MemoryRegion vtcm; DeviceState *glob_regs; + DeviceState *tlb; }; #endif From d33ec047c30d28c883cac0eaa4a7c9168cd81a5f Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:10 -0700 Subject: [PATCH 400/860] hw/hexagon: group the CPUs in a cluster The CPUs are now grouped in a TYPE_CPU_CLUSTER. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-6-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/Kconfig | 1 + hw/hexagon/hex-subsys.c | 30 +++++++++++++++++++++++++++++- hw/hexagon/hexagon_dsp.c | 11 ++++++++++- hw/hexagon/virt.c | 11 ++++++++++- include/hw/hexagon/hex-subsys.h | 12 +++++++++++- include/hw/hexagon/hexagon.h | 1 + 6 files changed, 62 insertions(+), 4 deletions(-) diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig index 52065ab3b2..121c548bbb 100644 --- a/hw/hexagon/Kconfig +++ b/hw/hexagon/Kconfig @@ -2,6 +2,7 @@ config HEX_DSP bool default y depends on HEXAGON + select CPU_CLUSTER config HEX_VIRT bool diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index ac7e29604e..6fad6fe0a4 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -10,6 +10,7 @@ #include "hw/hexagon/hex-subsys.h" #include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" +#include "hw/cpu/cluster.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" #include "hw/core/qdev.h" @@ -42,6 +43,16 @@ static DeviceState *tlb_create(HexagonCommonMachineState *hms, return tlb; } +static DeviceState *cluster_create(HexagonCommonMachineState *hms) +{ + DeviceState *cluster = qdev_new(TYPE_CPU_CLUSTER); + + object_property_add_child(OBJECT(hms), "cluster", OBJECT(cluster)); + qdev_prop_set_uint32(cluster, "cluster-id", 0); + + return cluster; +} + void hex_subsys_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg, Rev_t rev) { @@ -69,15 +80,32 @@ void hex_subsys_create(HexagonCommonMachineState *hms, &hms->vtcm); } + hms->cluster = cluster_create(hms); hms->glob_regs = globalreg_create(hms, m_cfg, rev); hms->tlb = tlb_create(hms, m_cfg); } -void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) +void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) { + object_property_add_child(OBJECT(hms->cluster), "cpu[*]", OBJECT(cpu)); object_property_set_link(OBJECT(cpu), "global-regs", OBJECT(hms->glob_regs), &error_fatal); object_property_set_link(OBJECT(cpu), "tlb", OBJECT(hms->tlb), &error_fatal); +} + +void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) +{ + /* + * The cluster must be realized after its CPUs have been parented into it + * (see hex_subsys_add_cpu()) but before any CPU is itself realized, since + * qdev_realize_and_unref() on a CPU latches cluster_index into the TCG + * cflags at that point. + */ + qdev_realize_and_unref(hms->cluster, NULL, &error_fatal); +} + +void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) +{ qdev_realize_and_unref(cpu, NULL, &error_fatal); } diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c index 25e8550fba..1db5d68166 100644 --- a/hw/hexagon/hexagon_dsp.c +++ b/hw/hexagon/hexagon_dsp.c @@ -118,6 +118,8 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, hex_subsys_create(hms, m_cfg, rev); + g_autofree HexagonCPU **cpus = g_new(HexagonCPU *, machine->smp.cpus); + for (int i = 0; i < machine->smp.cpus; i++) { HexagonCPU *cpu = HEXAGON_CPU(object_new(machine->cpu_type)); qemu_register_reset(do_cpu_reset, cpu); @@ -130,7 +132,14 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, if (i == 0) { hexagon_init_bootstrap(dms, cpu); } - hex_subsys_realize_cpu(hms, DEVICE(cpu)); + hex_subsys_add_cpu(hms, DEVICE(cpu)); + cpus[i] = cpu; + } + + hex_subsys_realize_cluster(hms); + + for (int i = 0; i < machine->smp.cpus; i++) { + hex_subsys_realize_cpu(hms, DEVICE(cpus[i])); } } diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index b9e0bbb704..dc1d5b6aa2 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -246,6 +246,8 @@ static void virt_init(MachineState *ms) fdt_add_hvx(vms, m_cfg); + g_autofree HexagonCPU **cpus = g_new(HexagonCPU *, ms->smp.cpus); + for (int i = 0; i < ms->smp.cpus; i++) { HexagonCPU *cpu = HEXAGON_CPU(object_new(ms->cpu_type)); qemu_register_reset(do_cpu_reset, cpu); @@ -261,7 +263,14 @@ static void virt_init(MachineState *ms) } qdev_prop_set_uint32(DEVICE(cpu), "htid", i); qdev_prop_set_bit(DEVICE(cpu), "start-powered-off", (i != 0)); - hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpu)); + hex_subsys_add_cpu(&vms->parent_obj, DEVICE(cpu)); + cpus[i] = cpu; + } + + hex_subsys_realize_cluster(&vms->parent_obj); + + for (int i = 0; i < ms->smp.cpus; i++) { + hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpus[i])); } fdt_add_cpu_nodes(vms); diff --git a/include/hw/hexagon/hex-subsys.h b/include/hw/hexagon/hex-subsys.h index 087c105cfa..6d0d5e1da8 100644 --- a/include/hw/hexagon/hex-subsys.h +++ b/include/hw/hexagon/hex-subsys.h @@ -15,7 +15,17 @@ void hex_subsys_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg, Rev_t rev); -/* Realize a CPU into the subsystem. */ +/* + * Parent a CPU into the subsystem's cluster and wire its links. Call for + * every CPU before hex_subsys_realize_cluster(), then realize each CPU with + * hex_subsys_realize_cpu(). + */ +void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu); + +/* Realize the CPU cluster, once all CPUs have been parented into it. */ +void hex_subsys_realize_cluster(HexagonCommonMachineState *hms); + +/* Realize a CPU previously parented via hex_subsys_add_cpu(). */ void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu); #endif /* HW_HEXAGON_HEX_SUBSYS_H */ diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index 6edd93f478..31669a829f 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -157,6 +157,7 @@ struct HexagonCommonMachineState { MemoryRegion ram; MemoryRegion cfgtable_rom; MemoryRegion vtcm; + DeviceState *cluster; DeviceState *glob_regs; DeviceState *tlb; }; From d922df10b27aa9f492638a22b62a43e873246c40 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:11 -0700 Subject: [PATCH 401/860] bitops.h: Add find_first_bit32() set_bit32()/test_bit32()/etc already let devices operate on guest-visible uint32_t register arrays without depending on the host's 'unsigned long' size. Add find_first_bit32() so callers need not cast a uint32_t array to 'unsigned long *'. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-7-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- include/qemu/bitops.h | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/include/qemu/bitops.h b/include/qemu/bitops.h index c7b838a628..f7363a5479 100644 --- a/include/qemu/bitops.h +++ b/include/qemu/bitops.h @@ -43,10 +43,9 @@ * be some guest-visible register view of the bit array. * * We do not currently implement uint32_t versions of find_last_bit(), - * find_next_bit(), find_next_zero_bit(), find_first_bit() or - * find_first_zero_bit(), because we haven't yet needed them. If you - * need them you should implement them similarly to the 'unsigned long' - * versions. + * find_next_bit(), find_next_zero_bit() or find_first_zero_bit(), + * because we haven't yet needed them. If you need them you should + * implement them similarly to the 'unsigned long' versions. * * You can declare a bitmap to be used with these functions via the * DECLARE_BITMAP and DECLARE_BITMAP32 macros in bitmap.h. @@ -382,6 +381,29 @@ static inline int test_bit32(long nr, const uint32_t *addr) return 1U & (addr[BIT32_WORD(nr)] >> (nr & 31)); } +/** + * find_first_bit32 - find the first set bit in a memory region + * @addr: The address to start the search at + * @size: The maximum size to search + * + * Returns the bit number of the first set bit, + * or @size if there is no set bit in the bitmap. + */ +static inline uint32_t find_first_bit32(const uint32_t *addr, uint32_t size) +{ + uint32_t result; + + for (result = 0; result < size; result += 32) { + uint32_t tmp = *addr++; + if (tmp) { + result += ctz32(tmp); + return result < size ? result : size; + } + } + /* Not found */ + return size; +} + /** * DOC: Miscellaneous bit operations on single values * From 04f398b2a25d0b16339da4d0683ff505e2613ddf Mon Sep 17 00:00:00 2001 From: Sid Manning Date: Wed, 5 Aug 2026 21:27:12 -0700 Subject: [PATCH 402/860] hw/intc: Add l2vic interrupt controller The Hexagon DSP requires an L2VIC to route up to 1024 external interrupt sources through 4 VID output groups into the core's 8 interrupt inputs. Add a device model for it. Co-authored-by: Matheus Tavares Bernardino Co-authored-by: Damien Hedde Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-8-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- MAINTAINERS | 3 + docs/devel/hexagon-l2vic.rst | 55 ++++ docs/devel/index-internals.rst | 1 + hw/hexagon/Kconfig | 1 + hw/intc/Kconfig | 3 + hw/intc/hex-l2vic.c | 556 +++++++++++++++++++++++++++++++++ hw/intc/meson.build | 2 + hw/intc/trace-events | 4 + include/hw/intc/hex-l2vic.h | 61 ++++ target/hexagon/cpu.c | 2 + target/hexagon/cpu.h | 2 + target/hexagon/op_helper.c | 16 +- 12 files changed, 705 insertions(+), 1 deletion(-) create mode 100644 docs/devel/hexagon-l2vic.rst create mode 100644 hw/intc/hex-l2vic.c create mode 100644 include/hw/intc/hex-l2vic.h diff --git a/MAINTAINERS b/MAINTAINERS index 4628793dde..5a772818bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -250,6 +250,8 @@ M: Brian Cain R: Pierrick Bouvier S: Supported F: target/hexagon/ +F: hw/intc/hex-l2vic.c +F: include/hw/intc/hex-l2vic.h X: target/hexagon/idef-parser/ X: target/hexagon/gen_idef_parser_funcs.py F: linux-user/hexagon/ @@ -262,6 +264,7 @@ F: gdbstub/gdb-xml/hexagon*.xml F: docs/system/target-hexagon.rst F: docs/system/hexagon/ F: docs/devel/hexagon-sys.rst +F: docs/devel/hexagon-l2vic.rst T: git https://github.com/qualcomm/qemu.git hex-next Hexagon idef-parser diff --git a/docs/devel/hexagon-l2vic.rst b/docs/devel/hexagon-l2vic.rst new file mode 100644 index 0000000000..9cb2a86871 --- /dev/null +++ b/docs/devel/hexagon-l2vic.rst @@ -0,0 +1,55 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Hexagon L2 Vectored Interrupt Controller +======================================== + + +.. code-block:: none + + +-------------+ +----------------------+ + | l2vic | | hexagon core | + | | | | + IRQ in ---->| | | | + IRQ in ---->| VID0 -|----------------->| irq2 | + ... ---->| | | | | + IRQ in ---->| | | v | + | ... | | | + | | | / | | \ | + IRQ in ---->| | | t0 t1 t2 t3 ...| + IRQ in ---->| VIDN -| | | + ... ---->| | | | + IRQ in ---->| | | Global SREG File | + | | | | + | State | | | + | [ ] <--|==================|==> [ VID ] | + | [ ] <--|==================|==> [ VID1 ] | + | | | | + +-------------+ +----------------------+ + +L2VIC/Core Integration +---------------------- + +* hexagon core supports 8 external interrupt sources +* l2vic supports 1024 input interrupts mapped among 4 output interrupts +* l2vic has four output signals: { VID0, VID1, VID2, VID3 } +* l2vic device has a bank of registers per-VID that can be used to query + the status or assert new interrupts. +* Interrupts are 'steered' to threads based on { thread priority, 'EX' state, + thread interrupt mask, thread interrupt enable, global interrupt enable, + etc. }. +* Any hardware thread could conceivably handle any input interrupt, dependent + on state. +* The system register transfer instruction can read the VID0-VID3 values from + the l2vic when reading from hexagon core system registers "VID" and "VID1". +* When l2vic VID0 has multiple active interrupts, it pulses the VID0 output + IRQ and stores the IRQ number for the VID0 register field. Only after this + interrupt is cleared can the l2vic pulse the VID0 output IRQ again and provide + the next interrupt number on the VID0 register. +* The ``ciad`` instruction clears the l2vic input interrupt and un-disables the + core interrupt. If some/an l2vic VID0 interrupt is pending when this occurs, + the next interrupt should fire and any subsequent reads of the VID register + should reflect the newly raised interrupt. +* In QEMU, on an external interrupt or an unmasked-pending interrupt, + all vCPUs are triggered (has_work==true) and each will grab the IO lock + while considering the steering logic to determine whether they're the thread + that must handle the interrupt. diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst index a8f5e310df..763cda1e76 100644 --- a/docs/devel/index-internals.rst +++ b/docs/devel/index-internals.rst @@ -15,6 +15,7 @@ Details about QEMU's various subsystems including how to add features to them. clocks ebpf_rss hexagon-sys + hexagon-l2vic migration/index multi-process reset diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig index 121c548bbb..c75090c44a 100644 --- a/hw/hexagon/Kconfig +++ b/hw/hexagon/Kconfig @@ -3,6 +3,7 @@ config HEX_DSP default y depends on HEXAGON select CPU_CLUSTER + select HEX_L2VIC config HEX_VIRT bool diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 636d00b7e8..097de4efc5 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -8,6 +8,9 @@ config I8259 config PL190 bool +config HEX_L2VIC + bool + config IOAPIC bool select I8259 diff --git a/hw/intc/hex-l2vic.c b/hw/intc/hex-l2vic.c new file mode 100644 index 0000000000..f07ec850d4 --- /dev/null +++ b/hw/intc/hex-l2vic.c @@ -0,0 +1,556 @@ +/* + * QEMU L2VIC Interrupt Controller + * + * Arm PrimeCell PL190 Vector Interrupt Controller was used as a reference. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/irq.h" +#include "hw/core/sysbus.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/bitmap.h" +#include "qemu/bitops.h" +#include "hw/intc/hex-l2vic.h" +#include "trace.h" + +#define L2VIC_VID_GRP_0 0x0 /* Read */ +#define L2VIC_VID_GRP_1 0x4 /* Read */ +#define L2VIC_VID_GRP_2 0x8 /* Read */ +#define L2VIC_VID_GRP_3 0xC /* Read */ +#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */ +#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */ +#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */ +#define L2VIC_INT_TYPEn 0x280 /* Read/Write */ +#define L2VIC_INT_STATUSn 0x380 /* Read */ +#define L2VIC_INT_CLEARn 0x400 /* Write */ +#define L2VIC_SOFT_INTn 0x480 /* Write */ +#define L2VIC_INT_PENDINGn 0x500 /* Read */ +#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */ +#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */ +#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */ +#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */ + +#define L2VIC_INTERRUPT_MAX 1024 +/* + * Note about l2vic groups: + * Each interrupt to L2VIC can be configured to associate with one of + * four groups. + * Group 0 interrupts go to IRQ2 via VID 0 (SSR: 0xC2, the default) + * Group 1 interrupts go to IRQ3 via VID 1 (SSR: 0xC3) + * Group 2 interrupts go to IRQ4 via VID 2 (SSR: 0xC4) + * Group 3 interrupts go to IRQ5 via VID 3 (SSR: 0xC5) + */ + +static void bitmap32_write_word(uint32_t *bitmap, int word_offset, uint32_t val) +{ + bitmap[word_offset] = val; +} + +static void bitmap32_clear_word(uint32_t *bitmap, int word_offset, + uint32_t mask) +{ + bitmap[word_offset] &= ~mask; +} + +static void bitmap32_set_word(uint32_t *bitmap, int word_offset, uint32_t mask) +{ + bitmap[word_offset] |= mask; +} + +static uint32_t bitmap32_read_word(uint32_t *bitmap, int word_offset) +{ + return bitmap[word_offset]; +} + +OBJECT_DECLARE_SIMPLE_TYPE(HexL2VICState, HEX_L2VIC) + +#define SLICE_MAX (L2VIC_INTERRUPT_MAX / 32) +#define L2VIC_REG_RANGE_SIZE 0x80 + +typedef struct HexL2VICState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion fast_iomem; + /* + * vid_group[i] is readable at L2VIC_VID_GRP_i (offset i*4): the irq + * last delivered through VID group i, 0-1023 so only 10 bits are used. + */ + uint32_t vid_group[4]; + /* + * Last irq delivered on any VID group; not specific to group 0. + * Used by the ciad path to clear the most-recently-delivered + * interrupt from int_status. + */ + uint32_t vid; + DECLARE_BITMAP32(int_enable, L2VIC_INTERRUPT_MAX); + /* Asserted interrupts awaiting delivery once no VID is active */ + DECLARE_BITMAP32(int_pending, L2VIC_INTERRUPT_MAX); + /* Which enabled interrupt is active */ + DECLARE_BITMAP32(int_status, L2VIC_INTERRUPT_MAX); + /* Edge or Level interrupt */ + DECLARE_BITMAP32(int_type, L2VIC_INTERRUPT_MAX); + DECLARE_BITMAP32(int_group_n[4], L2VIC_INTERRUPT_MAX); + qemu_irq irq[8]; +} HexL2VICState; + +typedef enum { + L2VIC_OP_WRITE, + L2VIC_OP_CLEAR, + L2VIC_OP_SET, + L2VIC_OP_NONE, +} L2VicWriteOp; + +typedef struct { + hwaddr base; + size_t state_offset; + L2VicWriteOp write_op; + bool write_only; +} L2VicRegRange; + +static const L2VicRegRange l2vic_reg_ranges[] = { + { L2VIC_INT_ENABLEn, offsetof(HexL2VICState, int_enable), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_ENABLE_CLEARn, offsetof(HexL2VICState, int_enable), + L2VIC_OP_CLEAR, true }, + { L2VIC_INT_ENABLE_SETn, offsetof(HexL2VICState, int_enable), + L2VIC_OP_SET, true }, + { L2VIC_INT_TYPEn, offsetof(HexL2VICState, int_type), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_STATUSn, offsetof(HexL2VICState, int_status), + L2VIC_OP_NONE, false }, + { L2VIC_INT_CLEARn, offsetof(HexL2VICState, int_status), + L2VIC_OP_CLEAR, true }, + { L2VIC_SOFT_INTn, offsetof(HexL2VICState, int_pending), + L2VIC_OP_NONE, true }, + { L2VIC_INT_PENDINGn, offsetof(HexL2VICState, int_pending), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_0, offsetof(HexL2VICState, int_group_n[0]), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_1, offsetof(HexL2VICState, int_group_n[1]), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_2, offsetof(HexL2VICState, int_group_n[2]), + L2VIC_OP_WRITE, false }, + { L2VIC_INT_GRPn_3, offsetof(HexL2VICState, int_group_n[3]), + L2VIC_OP_WRITE, false }, +}; + +static uint32_t *l2vic_state_bitmap(HexL2VICState *s, size_t state_offset) +{ + return (uint32_t *)((char *)s + state_offset); +} + +static bool l2vic_reg_read_range(HexL2VICState *s, hwaddr offset, + uint64_t *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) { + const L2VicRegRange *r = &l2vic_reg_ranges[i]; + + if (offset >= r->base && + offset < r->base + L2VIC_REG_RANGE_SIZE) { + if (r->write_only) { + *value = 0; + } else { + uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset); + *value = bitmap32_read_word(bitmap, + (offset - r->base) >> 2); + } + return true; + } + } + return false; +} + +static bool l2vic_reg_write_range(HexL2VICState *s, hwaddr offset, + uint32_t val) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(l2vic_reg_ranges); i++) { + const L2VicRegRange *r = &l2vic_reg_ranges[i]; + + if (offset >= r->base && + offset < r->base + L2VIC_REG_RANGE_SIZE) { + uint32_t *bitmap = l2vic_state_bitmap(s, r->state_offset); + int word = (offset - r->base) >> 2; + + switch (r->write_op) { + case L2VIC_OP_WRITE: + bitmap32_write_word(bitmap, word, val); + break; + case L2VIC_OP_CLEAR: + bitmap32_clear_word(bitmap, word, val); + break; + case L2VIC_OP_SET: + bitmap32_set_word(bitmap, word, val); + break; + case L2VIC_OP_NONE: + /* Read-only or handled elsewhere; ignore the write. */ + break; + default: + g_assert_not_reached(); + } + return true; + } + } + return false; +} + +/* + * The four INT_GRPn_* register arrays are interleaved across irqs in + * blocks of 8: irq 0-7 live in group_n[0], irq 8-15 in group_n[1], irq + * 16-23 in group_n[2], irq 24-31 in group_n[3], irq 32-39 back in + * group_n[0], and so on. + */ +static uint32_t *get_int_group(HexL2VICState *s, int irq) +{ + return s->int_group_n[extract32(irq, 3, 2)]; +} + +static int find_slice(int irq) +{ + return irq / 32; +} + +static int get_vid(HexL2VICState *s, int irq) +{ + uint32_t *group = get_int_group(s, irq); + uint32_t slice = group[find_slice(irq)]; + uint32_t vid; + /* + * Each irq occupies a 4-bit field: bit 3 is the group-enable bit, + * bits 0-2 select the VID group. Shift down to this irq's field. + */ + uint32_t val = slice >> ((irq & 0x7) * 4); + + if (!(val & 0x8)) { + return 0; + } + vid = val & 0x7; + if (vid >= ARRAY_SIZE(s->vid_group)) { + qemu_log_mask(LOG_GUEST_ERROR, + "L2VIC: irq %d requests invalid vid group %u\n", + irq, vid); + return 0; + } + return vid; +} + +static inline bool vid_active(HexL2VICState *s) +{ + const uint32_t size = L2VIC_INTERRUPT_MAX; + const uint32_t active_irq = find_first_bit32(s->int_status, size); + return active_irq != size; +} + +static bool l2vic_update(HexL2VICState *s, int irq) +{ + bool pending; + bool enable; + + if (vid_active(s)) { + return true; + } + + pending = test_bit32(irq, s->int_pending); + enable = test_bit32(irq, s->int_enable); + if (pending && enable) { + int vid = get_vid(s, irq); + set_bit32(irq, s->int_status); + clear_bit32(irq, s->int_pending); + /* + * Only auto-disable for edge-triggered interrupts (type=1). + * Level-triggered interrupts (type=0, the default) keep their + * enable bit set across deliveries -- the firmware enables once + * and expects the interrupt to remain enabled. + */ + if (test_bit32(irq, s->int_type)) { + clear_bit32(irq, s->int_enable); + } + s->vid = irq; + s->vid_group[vid] = irq; + + qemu_irq_pulse(s->irq[vid + 2]); + trace_hex_l2vic_delivered(irq, vid); + return true; + } + return false; +} + +static void l2vic_update_all(HexL2VICState *s) +{ + for (int i = 0; i < L2VIC_INTERRUPT_MAX; i++) { + if (l2vic_update(s, i)) { + /* once vid is active, no-one else can set it until ciad */ + return; + } + } +} + +static void l2vic_set_irq(void *opaque, int irq, int level) +{ + HexL2VICState *s = (HexL2VICState *)opaque; + + if (level) { + set_bit32(irq, s->int_pending); + } + l2vic_update(s, irq); +} + +static void l2vic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + HexL2VICState *s = (HexL2VICState *)opaque; + + trace_hex_l2vic_reg_write((unsigned)offset, (uint32_t)val); + + if (!l2vic_reg_write_range(s, offset, val)) { + qemu_log_mask(LOG_UNIMP, + "%s: offset 0x%" HWADDR_PRIx " unimplemented\n", + __func__, offset); + } + + /* SOFT_INT also sets pending for edge-triggered interrupts */ + if (offset >= L2VIC_SOFT_INTn && + offset < L2VIC_SOFT_INTn + L2VIC_REG_RANGE_SIZE && val) { + int base_irq = ((offset - L2VIC_SOFT_INTn) >> 2) * 32; + uint32_t bits = val; + int bit; + + while ((bit = ctz32(bits)) < 32) { + int irq = base_irq + bit; + + if (test_bit32(irq, s->int_type)) { + set_bit32(irq, s->int_pending); + } + bits &= ~(1u << bit); + } + } + + l2vic_update_all(s); +} + +static uint64_t l2vic_read(void *opaque, hwaddr offset, unsigned size) +{ + uint64_t value; + HexL2VICState *s = (HexL2VICState *)opaque; + + if (offset <= L2VIC_VID_GRP_3) { + value = s->vid_group[offset >> 2]; + } else if (!l2vic_reg_read_range(s, offset, &value)) { + value = 0; + qemu_log_mask(LOG_GUEST_ERROR, + "L2VIC: %s: offset 0x%" HWADDR_PRIx "\n", __func__, + offset); + } + + trace_hex_l2vic_reg_read((unsigned)offset, (uint32_t)value); + return value; +} + +static const MemoryRegionOps l2vic_ops = { + .read = l2vic_read, + .write = l2vic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +#define FASTL2VIC_ENABLE 0x0 +#define FASTL2VIC_DISABLE 0x1 +#define FASTL2VIC_INT 0x2 + +static void fastl2vic_write(void *opaque, hwaddr offset, uint64_t val, + unsigned size) +{ + if (offset == 0) { + uint32_t cmd = (val >> 16) & 0x3; + uint32_t irq = val & 0x3ff; + uint32_t slice = (irq / 32) * 4; + val = 1 << (irq % 32); + + if (cmd == FASTL2VIC_ENABLE) { + l2vic_write(opaque, L2VIC_INT_ENABLE_SETn + slice, val, size); + } else if (cmd == FASTL2VIC_DISABLE) { + l2vic_write(opaque, L2VIC_INT_ENABLE_CLEARn + slice, val, size); + } else if (cmd == FASTL2VIC_INT) { + l2vic_write(opaque, L2VIC_SOFT_INTn + slice, val, size); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid write cmd %" PRId32 "\n", + __func__, cmd); + } + return; + } + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid write offset 0x%08" HWADDR_PRIx + "\n", __func__, offset); +} + +static uint64_t fastl2vic_read(void *opaque, hwaddr offset, unsigned size) +{ + return 0; +} + +static const MemoryRegionOps fastl2vic_ops = { + .read = fastl2vic_read, + .write = fastl2vic_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .valid.unaligned = false, +}; + +static uint32_t l2vic_interface_read_vid_impl(HexL2VicInterface *iface, + uint32_t group) +{ + HexL2VICState *s = HEX_L2VIC(iface); + uint32_t result = 0; + + if (group == 0) { + /* VID register combines vid_group[0] (VID0) and vid_group[1] (VID1) */ + result = deposit32(result, 0, 16, s->vid_group[0]); + result = deposit32(result, 16, 16, s->vid_group[1]); + } else if (group == 1) { + /* VID1 register combines vid_group[2] (VID2) and vid_group[3] (VID3) */ + result = deposit32(result, 0, 16, s->vid_group[2]); + result = deposit32(result, 16, 16, s->vid_group[3]); + } + return result; +} + +static void l2vic_interface_update_vid_impl(HexL2VicInterface *iface, + uint32_t group, uint32_t value) +{ + HexL2VICState *s = HEX_L2VIC(iface); + + if (group == 0) { + s->vid_group[0] = extract32(value, 0, 16); + s->vid_group[1] = extract32(value, 16, 16); + } else if (group == 1) { + s->vid_group[2] = extract32(value, 0, 16); + s->vid_group[3] = extract32(value, 16, 16); + } + + l2vic_update_all(s); +} + +static void l2vic_interface_clear_interrupt_impl(HexL2VicInterface *iface) +{ + HexL2VICState *s = HEX_L2VIC(iface); + + if (s->vid < L2VIC_INTERRUPT_MAX) { + clear_bit32(s->vid, s->int_status); + } + l2vic_update_all(s); +} + +static void l2vic_reset_hold(Object *obj, ResetType type G_GNUC_UNUSED) +{ + HexL2VICState *s = HEX_L2VIC(obj); + + memset(s->int_enable, 0, sizeof(s->int_enable)); + memset(s->int_pending, 0, sizeof(s->int_pending)); + memset(s->int_status, 0, sizeof(s->int_status)); + memset(s->int_type, 0, sizeof(s->int_type)); + memset(s->int_group_n, 0, sizeof(s->int_group_n)); + memset(s->vid_group, 0, sizeof(s->vid_group)); + s->vid = 0; + + l2vic_update_all(s); +} + +static void reset_irq_handler(void *opaque, int irq, int level) +{ + Object *obj = OBJECT(opaque); + + if (level) { + l2vic_reset_hold(obj, RESET_TYPE_COLD); + } +} + +static void l2vic_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + HexL2VICState *s = HEX_L2VIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &l2vic_ops, s, "l2vic", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + memory_region_init_io(&s->fast_iomem, obj, &fastl2vic_ops, s, "fast", + 0x10000); + sysbus_init_mmio(sbd, &s->fast_iomem); + + qdev_init_gpio_in(dev, l2vic_set_irq, L2VIC_INTERRUPT_MAX); + qdev_init_gpio_in_named(dev, reset_irq_handler, "reset", 1); + for (i = 0; i < 8; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } +} + +static const VMStateDescription vmstate_l2vic = { + .name = "l2vic", + .version_id = 1, + .minimum_version_id = 1, + .fields = + (VMStateField[]){ + VMSTATE_UINT32_ARRAY(vid_group, HexL2VICState, 4), + VMSTATE_UINT32(vid, HexL2VICState), + VMSTATE_UINT32_ARRAY(int_enable, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_type, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_status, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_ARRAY(int_pending, HexL2VICState, SLICE_MAX), + VMSTATE_UINT32_2DARRAY(int_group_n, HexL2VICState, 4, SLICE_MAX), + VMSTATE_END_OF_LIST() } +}; + +static void l2vic_interface_class_init(ObjectClass *klass, const void *data) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_CLASS(klass); + + k->read_vid = l2vic_interface_read_vid_impl; + k->update_vid = l2vic_interface_update_vid_impl; + k->clear_interrupt = l2vic_interface_clear_interrupt_impl; +} + +static void l2vic_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_l2vic; + rc->phases.hold = l2vic_reset_hold; +} + +static const TypeInfo l2vic_interface_info = { + .name = TYPE_HEX_L2VIC_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(HexL2VicInterfaceClass), + .class_init = l2vic_interface_class_init, +}; + +static const TypeInfo l2vic_info = { + .name = TYPE_HEX_L2VIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(HexL2VICState), + .instance_init = l2vic_init, + .class_init = l2vic_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HEX_L2VIC_INTERFACE }, + { } + }, +}; + +static const TypeInfo l2vic_types[] = { + l2vic_interface_info, + l2vic_info, +}; + +DEFINE_TYPES(l2vic_types) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 377de78bb4..a1401cb521 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -74,6 +74,8 @@ specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('xics_spapr.c', 'spapr_xi specific_ss.add(when: 'CONFIG_XIVE', if_true: files('xive.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) + +specific_ss.add(when: 'CONFIG_HEX_L2VIC', if_true: files('hex-l2vic.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e7d6c30448..7512c6e600 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -337,6 +337,10 @@ sh_intc_register(const char *s, int id, unsigned short v, int c, int m) "%s %u - sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " -> 0x%lx" sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx" sh_intc_set(int id, int enable) "setting interrupt group %d to %d" +# hex-l2vic.c +hex_l2vic_reg_write(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32 +hex_l2vic_reg_read(unsigned int addr, uint32_t value) "addr: 0x%03x value: 0x%08"PRIx32 +hex_l2vic_delivered(int irq, int vid) "l2vic: delivered %d (vid %d)" # loongson_ipi.c loongson_ipi_read(unsigned size, uint64_t addr, uint64_t val) "size: %u addr: 0x%"PRIx64 "val: 0x%"PRIx64 diff --git a/include/hw/intc/hex-l2vic.h b/include/hw/intc/hex-l2vic.h new file mode 100644 index 0000000000..edc278266c --- /dev/null +++ b/include/hw/intc/hex-l2vic.h @@ -0,0 +1,61 @@ +/* + * QEMU L2VIC Interrupt Controller + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_INTC_HEX_L2VIC_H +#define HW_INTC_HEX_L2VIC_H + +#include "qom/object.h" + +#define TYPE_HEX_L2VIC "hex-l2vic" +/* + * L2VIC Interface for CPU/GlobalReg interaction + */ +#define TYPE_HEX_L2VIC_INTERFACE "hex-l2vic-if" + +typedef struct HexL2VicInterface HexL2VicInterface; + +typedef struct HexL2VicInterfaceClass { + InterfaceClass parent_class; + + uint32_t (*read_vid)(HexL2VicInterface *l2vic, uint32_t group); + + /* + * Write the VID: unpack the fields into per-group VIDs. This does + * not deliver or clear any interrupt; a pending interrupt stays + * gated until ciad. + */ + void (*update_vid)(HexL2VicInterface *l2vic, uint32_t group, + uint32_t value); + + /* Clear interrupt using CIAD instruction */ + void (*clear_interrupt)(HexL2VicInterface *l2vic); +} HexL2VicInterfaceClass; + +DECLARE_OBJ_CHECKERS(HexL2VicInterface, HexL2VicInterfaceClass, + HEX_L2VIC_INTERFACE, TYPE_HEX_L2VIC_INTERFACE); + +static inline uint32_t l2vic_read_vid(HexL2VicInterface *l2vic, + uint32_t group) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic); + return k->read_vid(l2vic, group); +} + +static inline void l2vic_update_vid(HexL2VicInterface *l2vic, uint32_t group, + uint32_t value) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic); + k->update_vid(l2vic, group, value); +} + +static inline void l2vic_clear_interrupt(HexL2VicInterface *l2vic) +{ + HexL2VicInterfaceClass *k = HEX_L2VIC_INTERFACE_GET_CLASS(l2vic); + k->clear_interrupt(l2vic); +} + +#endif /* HW_INTC_HEX_L2VIC_H */ diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 12d27ce138..75cae45029 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -65,6 +65,8 @@ static const Property hexagon_cpu_properties[] = { DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB, HexagonTLBState *), DEFINE_PROP_UINT32("exec-start-addr", HexagonCPU, boot_addr, 0xffffffff), + DEFINE_PROP_LINK("l2vic", HexagonCPU, l2vic, + TYPE_HEX_L2VIC_INTERFACE, HexL2VicInterface *), DEFINE_PROP_LINK("global-regs", HexagonCPU, globalregs, TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *), DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0), diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 7694fd91fa..f6c3c63932 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -39,6 +39,7 @@ typedef struct HexagonGlobalRegState HexagonGlobalRegState; #include "qemu/bitmap.h" #include "target/hexagon/reg_fields.h" +#include "hw/intc/hex-l2vic.h" #define NUM_PREGS 4 #define TOTAL_PER_THREAD_REGS 64 @@ -198,6 +199,7 @@ struct ArchCPU { uint32_t boot_addr; HexagonGlobalRegState *globalregs; uint32_t htid; + HexL2VicInterface *l2vic; #endif }; diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 3ce223caba..8fb3d43c33 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -40,6 +40,7 @@ #include "hw/hexagon/hexagon_globalreg.h" #include "hex_mmu.h" #include "hw/hexagon/hexagon_tlb.h" +#include "hw/intc/hex-l2vic.h" #include "hex_interrupts.h" #include "hexswi.h" #endif @@ -1564,7 +1565,20 @@ void HELPER(raise_stack_overflow)(CPUHexagonState *env, uint32_t slot, void HELPER(ciad)(CPUHexagonState *env, uint32_t mask) { - g_assert_not_reached(); + uint32_t ipendad; + uint32_t iad; + HexagonCPU *cpu; + + BQL_LOCK_GUARD(); + cpu = env_archcpu(env); + ipendad = hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD, + env->threadId); + iad = fGET_FIELD(ipendad, IPENDAD_IAD); + fSET_FIELD(ipendad, IPENDAD_IAD, iad & ~(mask)); + hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD, + ipendad, env->threadId); + l2vic_clear_interrupt(cpu->l2vic); + hex_interrupt_update(env); } void HELPER(siad)(CPUHexagonState *env, uint32_t mask) From df9c6d6396d08204674da2a81dfdc03b28bfda19 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:13 -0700 Subject: [PATCH 403/860] hw/hexagon: extract get_reg_value/set_reg_value stubs in globalreg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Route the globalreg read/write accessors through new get_reg_value()/set_reg_value() helpers instead of touching s->regs[reg] directly. This will be exploited by a subsequent patch that redirects VID/VID1 accesses to the L2VIC. Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-9-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/hexagon_globalreg.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c index b5e5913507..105475219a 100644 --- a/hw/hexagon/hexagon_globalreg.c +++ b/hw/hexagon/hexagon_globalreg.c @@ -135,6 +135,17 @@ static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val, return new_val; } +static uint32_t get_reg_value(HexagonGlobalRegState *s, uint32_t reg) +{ + return s->regs[reg]; +} + +static void set_reg_value(HexagonGlobalRegState *s, uint32_t reg, + uint32_t value) +{ + s->regs[reg] = value; +} + uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, uint32_t htid) { @@ -146,7 +157,7 @@ uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, g_assert(reg < NUM_SREGS); g_assert(reg >= HEX_SREG_GLB_START); - value = s->regs[reg]; + value = get_reg_value(s, reg); trace_hexagon_globalreg_read(htid, get_sreg_name(reg), value); return value; @@ -160,7 +171,7 @@ void hexagon_globalreg_write(HexagonGlobalRegState *s, uint32_t reg, } g_assert(reg < NUM_SREGS); g_assert(reg >= HEX_SREG_GLB_START); - s->regs[reg] = value; + set_reg_value(s, reg, value); trace_hexagon_globalreg_write(htid, get_sreg_name(reg), value); } @@ -168,6 +179,7 @@ uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, uint32_t value) { uint32_t reg_mask; + uint32_t cur_val; if (!s) { return value; @@ -175,9 +187,10 @@ uint32_t hexagon_globalreg_masked_value(HexagonGlobalRegState *s, uint32_t reg, g_assert(reg < NUM_SREGS); g_assert(reg >= HEX_SREG_GLB_START); reg_mask = global_sreg_immut_masks[reg]; + cur_val = get_reg_value(s, reg); return reg_mask == IMMUTABLE ? - s->regs[reg] : - apply_write_mask(value, s->regs[reg], reg_mask); + cur_val : + apply_write_mask(value, cur_val, reg_mask); } void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, @@ -186,7 +199,7 @@ void hexagon_globalreg_write_masked(HexagonGlobalRegState *s, uint32_t reg, if (!s) { return; } - s->regs[reg] = hexagon_globalreg_masked_value(s, reg, value); + set_reg_value(s, reg, hexagon_globalreg_masked_value(s, reg, value)); } uint64_t hexagon_globalreg_get_pcycle_base(HexagonGlobalRegState *s) From 03f38eecc93b66520bd22b07d25c88e03db0ff41 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:14 -0700 Subject: [PATCH 404/860] hw/hexagon: connect l2vic device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the l2vic to the shared hex-subsys so both machine models pick it up. Map its register banks, wire the interrupt lines to CPU[0] and link each vCPU, globalregs. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-10-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/hex-subsys.c | 38 +++++++++++++++++++++++++- hw/hexagon/hexagon_dsp.c | 2 +- hw/hexagon/hexagon_globalreg.c | 25 +++++++++++++++++ hw/hexagon/virt.c | 2 +- include/hw/hexagon/hex-subsys.h | 5 ++-- include/hw/hexagon/hexagon.h | 1 + include/hw/hexagon/hexagon_globalreg.h | 4 +++ 7 files changed, 72 insertions(+), 5 deletions(-) diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index 6fad6fe0a4..25c028f657 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -10,6 +10,7 @@ #include "hw/hexagon/hex-subsys.h" #include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" +#include "hw/intc/hex-l2vic.h" #include "hw/cpu/cluster.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" @@ -17,6 +18,31 @@ #include "hw/core/sysbus.h" #include "system/address-spaces.h" +#define HEX_L2VIC_CPU_IRQS 8 + +static DeviceState *l2vic_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + DeviceState *l2vic = qdev_new(TYPE_HEX_L2VIC); + + object_property_add_child(OBJECT(hms), "l2vic", OBJECT(l2vic)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(l2vic), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 0, m_cfg->l2vic_base); + sysbus_mmio_map(SYS_BUS_DEVICE(l2vic), 1, + m_cfg->cfgtable.fastl2vic_base << 16); + + return l2vic; +} + +static void l2vic_connect_cpu(DeviceState *l2vic, DeviceState *cpu) +{ + int i; + + for (i = 0; i < HEX_L2VIC_CPU_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(l2vic), i, qdev_get_gpio_in(cpu, i)); + } +} + static DeviceState *globalreg_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg, Rev_t rev) @@ -26,6 +52,8 @@ static DeviceState *globalreg_create(HexagonCommonMachineState *hms, object_property_add_child(OBJECT(hms), "global-regs", OBJECT(glob_regs)); qdev_prop_set_uint64(glob_regs, "config-table-addr", m_cfg->cfgbase); qdev_prop_set_uint32(glob_regs, "dsp-rev", rev); + object_property_set_link(OBJECT(glob_regs), "l2vic", OBJECT(hms->l2vic), + &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs), &error_fatal); return glob_regs; @@ -81,6 +109,7 @@ void hex_subsys_create(HexagonCommonMachineState *hms, } hms->cluster = cluster_create(hms); + hms->l2vic = l2vic_create(hms, m_cfg); hms->glob_regs = globalreg_create(hms, m_cfg, rev); hms->tlb = tlb_create(hms, m_cfg); } @@ -92,6 +121,8 @@ void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) OBJECT(hms->glob_regs), &error_fatal); object_property_set_link(OBJECT(cpu), "tlb", OBJECT(hms->tlb), &error_fatal); + object_property_set_link(OBJECT(cpu), "l2vic", OBJECT(hms->l2vic), + &error_fatal); } void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) @@ -105,7 +136,12 @@ void hex_subsys_realize_cluster(HexagonCommonMachineState *hms) qdev_realize_and_unref(hms->cluster, NULL, &error_fatal); } -void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu) +void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, + bool boot_cpu) { qdev_realize_and_unref(cpu, NULL, &error_fatal); + + if (boot_cpu) { + l2vic_connect_cpu(hms->l2vic, cpu); + } } diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c index 1db5d68166..20306c28e7 100644 --- a/hw/hexagon/hexagon_dsp.c +++ b/hw/hexagon/hexagon_dsp.c @@ -139,7 +139,7 @@ static void hexagon_common_init(MachineState *machine, Rev_t rev, hex_subsys_realize_cluster(hms); for (int i = 0; i < machine->smp.cpus; i++) { - hex_subsys_realize_cpu(hms, DEVICE(cpus[i])); + hex_subsys_realize_cpu(hms, DEVICE(cpus[i]), (i == 0)); } } diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c index 105475219a..285cb48c44 100644 --- a/hw/hexagon/hexagon_globalreg.c +++ b/hw/hexagon/hexagon_globalreg.c @@ -11,6 +11,7 @@ #include "hw/core/qdev-properties.h" #include "hw/core/sysbus.h" #include "hw/core/resettable.h" +#include "hw/intc/hex-l2vic.h" #include "migration/vmstate.h" #include "qom/object.h" #include "target/hexagon/cpu.h" @@ -135,8 +136,16 @@ static inline uint32_t apply_write_mask(uint32_t new_val, uint32_t cur_val, return new_val; } +static inline bool is_vid_reg(uint32_t reg) +{ + return reg == HEX_SREG_VID || reg == HEX_SREG_VID1; +} + static uint32_t get_reg_value(HexagonGlobalRegState *s, uint32_t reg) { + if (is_vid_reg(reg)) { + return l2vic_read_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1); + } return s->regs[reg]; } @@ -144,6 +153,9 @@ static void set_reg_value(HexagonGlobalRegState *s, uint32_t reg, uint32_t value) { s->regs[reg] = value; + if (is_vid_reg(reg)) { + l2vic_update_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1, value); + } } uint32_t hexagon_globalreg_read(HexagonGlobalRegState *s, uint32_t reg, @@ -269,6 +281,16 @@ static void hexagon_globalreg_reset_hold(Object *obj, ResetType type) do_hexagon_globalreg_reset(s); } +static void hexagon_globalreg_realize(DeviceState *dev, Error **errp) +{ + HexagonGlobalRegState *s = HEXAGON_GLOBALREG(dev); + + if (!s->l2vic) { + error_setg(errp, "hexagon_globalreg: 'l2vic' link property not set"); + return; + } +} + static const VMStateDescription vmstate_hexagon_globalreg = { .name = "hexagon_globalreg", .version_id = 1, @@ -288,6 +310,8 @@ static const VMStateDescription vmstate_hexagon_globalreg = { }; static const Property hexagon_globalreg_properties[] = { + DEFINE_PROP_LINK("l2vic", HexagonGlobalRegState, l2vic, + TYPE_HEX_L2VIC_INTERFACE, HexL2VicInterface *), DEFINE_PROP_UINT32("boot-evb", HexagonGlobalRegState, boot_evb, 0x0), DEFINE_PROP_UINT64("config-table-addr", HexagonGlobalRegState, config_table_addr, 0xffffffffULL), @@ -308,6 +332,7 @@ static void hexagon_globalreg_class_init(ObjectClass *klass, const void *data) ResettableClass *rc = RESETTABLE_CLASS(klass); rc->phases.hold = hexagon_globalreg_reset_hold; + dc->realize = hexagon_globalreg_realize; dc->vmsd = &vmstate_hexagon_globalreg; dc->user_creatable = false; device_class_set_props(dc, hexagon_globalreg_properties); diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index dc1d5b6aa2..9a680852c3 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -270,7 +270,7 @@ static void virt_init(MachineState *ms) hex_subsys_realize_cluster(&vms->parent_obj); for (int i = 0; i < ms->smp.cpus; i++) { - hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpus[i])); + hex_subsys_realize_cpu(&vms->parent_obj, DEVICE(cpus[i]), (i == 0)); } fdt_add_cpu_nodes(vms); diff --git a/include/hw/hexagon/hex-subsys.h b/include/hw/hexagon/hex-subsys.h index 6d0d5e1da8..5792f7b5af 100644 --- a/include/hw/hexagon/hex-subsys.h +++ b/include/hw/hexagon/hex-subsys.h @@ -18,7 +18,7 @@ void hex_subsys_create(HexagonCommonMachineState *hms, /* * Parent a CPU into the subsystem's cluster and wire its links. Call for * every CPU before hex_subsys_realize_cluster(), then realize each CPU with - * hex_subsys_realize_cpu(). + * hex_subsys_realize_cpu(). CPU[0] receives the L2VIC outputs. */ void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu); @@ -26,6 +26,7 @@ void hex_subsys_add_cpu(HexagonCommonMachineState *hms, DeviceState *cpu); void hex_subsys_realize_cluster(HexagonCommonMachineState *hms); /* Realize a CPU previously parented via hex_subsys_add_cpu(). */ -void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu); +void hex_subsys_realize_cpu(HexagonCommonMachineState *hms, DeviceState *cpu, + bool boot_cpu); #endif /* HW_HEXAGON_HEX_SUBSYS_H */ diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index 31669a829f..ec1578807d 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -158,6 +158,7 @@ struct HexagonCommonMachineState { MemoryRegion cfgtable_rom; MemoryRegion vtcm; DeviceState *cluster; + DeviceState *l2vic; DeviceState *glob_regs; DeviceState *tlb; }; diff --git a/include/hw/hexagon/hexagon_globalreg.h b/include/hw/hexagon/hexagon_globalreg.h index 950099808f..397dc6854c 100644 --- a/include/hw/hexagon/hexagon_globalreg.h +++ b/include/hw/hexagon/hexagon_globalreg.h @@ -10,6 +10,7 @@ #include "hw/core/qdev.h" #include "hw/core/sysbus.h" +#include "hw/intc/hex-l2vic.h" #include "qom/object.h" #include "target/hexagon/cpu.h" @@ -22,6 +23,9 @@ struct HexagonGlobalRegState { /* Array of system registers */ uint32_t regs[NUM_SREGS]; + /* L2VIC interface used to back the VID/VID1 registers */ + HexL2VicInterface *l2vic; + /* Global performance cycle counter base */ uint64_t g_pcycle_base; From 76f40eb3f19311d2f83ba3bd2e1a778681359902 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:15 -0700 Subject: [PATCH 405/860] hw/hexagon/virt: instantiate virtio-mmio transports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a VIRT_MMIO region and virtio-mmio transports, wired into l2vic. Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-11-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/virt.c | 28 ++++++++++++++++++++++++++++ include/hw/hexagon/virt.h | 3 +++ 2 files changed, 31 insertions(+) diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index 9a680852c3..ff0770cfc4 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -30,11 +30,19 @@ enum { VIRT_UART0, + VIRT_MMIO, VIRT_FDT, }; +/* + * Virtio IRQs run from VIRTIO_IRQ_BASE to + * VIRTIO_IRQ_BASE + VIRTIO_DEV_COUNT - 1 + */ +static const int VIRTIO_IRQ_BASE = 16; + static const MemMapEntry base_memmap[] = { [VIRT_UART0] = { 0x10000000, 0x00000200 }, + [VIRT_MMIO] = { 0x11000000, 0x00001000 }, [VIRT_FDT] = { 0x99800000, 0x00400000 }, }; @@ -167,7 +175,25 @@ static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms) } } +static void create_virtio_devices(HexagonVirtMachineState *vms) +{ + hwaddr size = base_memmap[VIRT_MMIO].size; + for (int i = 0; i < VIRTIO_DEV_COUNT; i++) { + int irq = VIRTIO_IRQ_BASE + i; + hwaddr base = base_memmap[VIRT_MMIO].base + i * size; + DeviceState *dev = qdev_new("virtio-mmio"); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + + object_property_add_child(OBJECT(MACHINE(vms)), "virtio-mmio[*]", + OBJECT(dev)); + sysbus_realize_and_unref(s, &error_fatal); + sysbus_mmio_map(s, 0, base); + sysbus_connect_irq(s, 0, + qdev_get_gpio_in(vms->parent_obj.l2vic, irq)); + vms->virtio_mmio[i] = dev; + } +} void hexagon_load_fdt(const HexagonVirtMachineState *vms) { @@ -246,6 +272,8 @@ static void virt_init(MachineState *ms) fdt_add_hvx(vms, m_cfg); + create_virtio_devices(vms); + g_autofree HexagonCPU **cpus = g_new(HexagonCPU *, ms->smp.cpus); for (int i = 0; i < ms->smp.cpus; i++) { diff --git a/include/hw/hexagon/virt.h b/include/hw/hexagon/virt.h index cf0cd2f8f2..4ca2f1ef85 100644 --- a/include/hw/hexagon/virt.h +++ b/include/hw/hexagon/virt.h @@ -11,6 +11,8 @@ #include "hw/hexagon/hexagon.h" #include "target/hexagon/cpu.h" +#define VIRTIO_DEV_COUNT 8 + struct HexagonVirtMachineState { HexagonCommonMachineState parent_obj; @@ -19,6 +21,7 @@ struct HexagonVirtMachineState { MemoryRegion tcm; MemoryRegion bios; Clock *apb_clk; + DeviceState *virtio_mmio[VIRTIO_DEV_COUNT]; }; void hexagon_load_fdt(const struct HexagonVirtMachineState *vms); From abd9e918e79bc3ed860745890c35355b4ed85cc6 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:16 -0700 Subject: [PATCH 406/860] hw/hexagon/virt: add l2vic interrupt-controller and virtio-mmio FDT nodes Expose l2vic as a device-tree interrupt-controller node and reference it via interrupt-parent, so guest kernels can discover the virtio-mmio transports. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-12-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/virt.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index ff0770cfc4..ea20ea3c50 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -75,6 +75,30 @@ static void create_fdt(HexagonVirtMachineState *vms) qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed)); } +static int32_t fdt_add_l2vic(HexagonVirtMachineState *vms, + const struct hexagon_machine_config *m_cfg) +{ + MachineState *ms = MACHINE(vms); + int32_t l2vic_phandle = qemu_fdt_alloc_phandle(ms->fdt); + char *nodename = g_strdup_printf("/soc/interrupt-controller@%x", + m_cfg->l2vic_base); + const char compat[] = "qcom,h2-pic\0hvm-pic"; + + qemu_fdt_setprop_cell(ms->fdt, "/soc", "interrupt-parent", l2vic_phandle); + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 0x1); + qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, + m_cfg->l2vic_base, m_cfg->l2vic_size); + qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", l2vic_phandle); + + g_free(nodename); + return l2vic_phandle; +} + static void fdt_add_hvx(HexagonVirtMachineState *vms, const struct hexagon_machine_config *m_cfg) { @@ -175,13 +199,16 @@ static void fdt_add_cpu_nodes(const HexagonVirtMachineState *vms) } } -static void create_virtio_devices(HexagonVirtMachineState *vms) +static void create_virtio_devices(HexagonVirtMachineState *vms, + int32_t l2vic_phandle) { + MachineState *ms = MACHINE(vms); hwaddr size = base_memmap[VIRT_MMIO].size; for (int i = 0; i < VIRTIO_DEV_COUNT; i++) { int irq = VIRTIO_IRQ_BASE + i; hwaddr base = base_memmap[VIRT_MMIO].base + i * size; + char *nodename = g_strdup_printf("/soc/virtio_mmio@%" PRIx64, base); DeviceState *dev = qdev_new("virtio-mmio"); SysBusDevice *s = SYS_BUS_DEVICE(dev); @@ -192,6 +219,16 @@ static void create_virtio_devices(HexagonVirtMachineState *vms) sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->parent_obj.l2vic, irq)); vms->virtio_mmio[i] = dev; + + qemu_fdt_add_subnode(ms->fdt, nodename); + qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", + "virtio,mmio"); + qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", irq); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + l2vic_phandle); + + g_free(nodename); } } @@ -251,6 +288,7 @@ static void virt_init(MachineState *ms) HexagonVirtMachineState *vms = HEXAGON_VIRT_MACHINE(ms); const struct hexagon_machine_config *m_cfg = &v68n_1024; int32_t clk_phandle; + int32_t l2vic_phandle; create_fdt(vms); qemu_fdt_setprop_string(ms->fdt, "/chosen", "bootargs", ms->kernel_cmdline); @@ -272,7 +310,8 @@ static void virt_init(MachineState *ms) fdt_add_hvx(vms, m_cfg); - create_virtio_devices(vms); + l2vic_phandle = fdt_add_l2vic(vms, m_cfg); + create_virtio_devices(vms, l2vic_phandle); g_autofree HexagonCPU **cpus = g_new(HexagonCPU *, ms->smp.cpus); From 5f8e903c02404a8c880b4bfcfbe1589d0a36e911 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:17 -0700 Subject: [PATCH 407/860] hw/hexagon/virt: connect pl011 UART interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wire pl011's sysbus IRQ into l2vic at IRQ 15. Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-13-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/virt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/hexagon/virt.c b/hw/hexagon/virt.c index ea20ea3c50..64d8366d27 100644 --- a/hw/hexagon/virt.c +++ b/hw/hexagon/virt.c @@ -39,6 +39,7 @@ enum { * VIRTIO_IRQ_BASE + VIRTIO_DEV_COUNT - 1 */ static const int VIRTIO_IRQ_BASE = 16; +static const int VIRT_UART0_IRQ = 15; static const MemMapEntry base_memmap[] = { [VIRT_UART0] = { 0x10000000, 0x00000200 }, @@ -143,7 +144,7 @@ static int32_t fdt_add_clocks(const HexagonVirtMachineState *vms) } static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, - int32_t clk_phandle) + int32_t clk_phandle, int32_t l2vic_phandle) { char *nodename; hwaddr base = base_memmap[uart].base; @@ -161,6 +162,8 @@ static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, qdev_connect_clock_in(dev, "clk", vms->apb_clk); sysbus_realize_and_unref(s, &error_fatal); sysbus_mmio_map(s, 0, base); + sysbus_connect_irq(s, 0, + qdev_get_gpio_in(vms->parent_obj.l2vic, VIRT_UART0_IRQ)); nodename = g_strdup_printf("/pl011@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -168,6 +171,9 @@ static void fdt_add_uart(const HexagonVirtMachineState *vms, int uart, /* Note that we can't use setprop_string because of the embedded NUL */ qemu_fdt_setprop(ms->fdt, nodename, "compatible", compat, sizeof(compat)); qemu_fdt_setprop_cells(ms->fdt, nodename, "reg", 0, base, size); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupts", VIRT_UART0_IRQ); + qemu_fdt_setprop_cell(ms->fdt, nodename, "interrupt-parent", + l2vic_phandle); qemu_fdt_setprop_cells(ms->fdt, nodename, "clocks", clk_phandle, clk_phandle); qemu_fdt_setprop(ms->fdt, nodename, "clock-names", clocknames, @@ -342,7 +348,7 @@ static void virt_init(MachineState *ms) fdt_add_cpu_nodes(vms); clk_phandle = fdt_add_clocks(vms); - fdt_add_uart(vms, VIRT_UART0, clk_phandle); + fdt_add_uart(vms, VIRT_UART0, clk_phandle, l2vic_phandle); hexagon_load_fdt(vms); } From 8ff5b8d403483f76e36ffb30087aacaf1a41fe07 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:18 -0700 Subject: [PATCH 408/860] tests/qtest: add L2VIC qtest Add a qtest exercising L2VIC register access and interrupt enable/disable. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-14-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- MAINTAINERS | 1 + tests/qtest/l2vic-test.c | 249 +++++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 +- 3 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/l2vic-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 5a772818bb..85fc30503d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -252,6 +252,7 @@ S: Supported F: target/hexagon/ F: hw/intc/hex-l2vic.c F: include/hw/intc/hex-l2vic.h +F: tests/qtest/l2vic-test.c X: target/hexagon/idef-parser/ X: target/hexagon/gen_idef_parser_funcs.py F: linux-user/hexagon/ diff --git a/tests/qtest/l2vic-test.c b/tests/qtest/l2vic-test.c new file mode 100644 index 0000000000..adb4dc7281 --- /dev/null +++ b/tests/qtest/l2vic-test.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QTest testcase for the L2VIC Interrupt Controller + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "hw/hexagon/hexagon.h" + +#include "hw/hexagon/machine_cfg_v66g_1024.h.inc" +#include "hw/hexagon/machine_cfg_v68n_1024.h.inc" + +/* L2VIC register offsets exercised by this test */ +#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */ +#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */ +#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */ +#define L2VIC_INT_TYPEn 0x280 /* Read/Write */ +#define L2VIC_INT_STATUSn 0x380 /* Read */ +#define L2VIC_INT_CLEARn 0x400 /* Write */ +#define L2VIC_SOFT_INTn 0x480 /* Write */ +#define L2VIC_INT_PENDINGn 0x500 /* Read */ +#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */ +#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */ +#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */ +#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */ + +/* + * VID group readback: records which irq last fired through each VID + * group. Outputs themselves are momentary pulses (see l2vic_update()), + * so these registers -- not the qtest IRQ level snapshot -- are how the + * test observes VID steering. + */ +#define L2VIC_VID_GRP_0 0x0 +#define L2VIC_VID_GRP_1 0x4 +#define L2VIC_VID_GRP_2 0x8 +#define L2VIC_VID_GRP_3 0xC + +typedef struct { + const char *machine; + const struct hexagon_machine_config *cfg; +} L2VICMachineCfg; + +static const L2VICMachineCfg l2vic_machines[] = { + { "virt", &v68n_1024 }, + { "V66G_1024", &v66g_1024 }, +}; + +static uint32_t l2vic_read32(uint64_t base, uint32_t offset) +{ + return readl(base + offset); +} + +static void l2vic_write32(uint64_t base, uint32_t offset, uint32_t value) +{ + writel(base + offset, value); +} + +static void test_l2vic_register_access(uint64_t base) +{ + uint32_t val; + + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x1); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x1, ==, 0x1); + + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0x1); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x1, ==, 0x0); +} + +static void test_l2vic_interrupt_enable(uint64_t base) +{ + uint32_t val; + + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val, ==, 0); + + /* Enable IRQ 0 and 2 */ + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x5); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x5, ==, 0x5); + + /* Disable IRQ 0, leaving IRQ 2 enabled */ + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0x1); + val = l2vic_read32(base, L2VIC_INT_ENABLEn); + g_assert_cmpuint(val & 0x1, ==, 0x0); + g_assert_cmpuint(val & 0x4, ==, 0x4); +} + +static void test_l2vic_basic_functionality(uint64_t base) +{ + l2vic_read32(base, L2VIC_INT_ENABLEn); + l2vic_read32(base, L2VIC_INT_PENDINGn); + l2vic_read32(base, L2VIC_INT_STATUSn); + l2vic_read32(base, L2VIC_INT_TYPEn); + + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0); + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0); +} + +/* + * IRQs 0-7 pack their group-enable/VID-select nibbles into + * L2VIC_INT_GRPn_0 (int_group_n[0]), 4 bits per irq: bit 3 enables + * VID steering, bits 0-2 select the VID group (0-3), which pulses + * output line vid+2. + */ +static void l2vic_set_vid_group(uint64_t base, int irq, int vid) +{ + uint32_t val = l2vic_read32(base, L2VIC_INT_GRPn_0); + uint32_t nibble = 0x8 | (vid & 0x7); + + val &= ~(0xFu << (irq * 4)); + val |= nibble << (irq * 4); + l2vic_write32(base, L2VIC_INT_GRPn_0, val); +} + +static void test_l2vic_irq_outputs(uint64_t base) +{ + uint32_t val; + + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_TYPEn, 0); + l2vic_write32(base, L2VIC_INT_GRPn_0, 0); + + /* Group 0 / IRQ2: soft interrupts require edge-triggered config */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x1); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x1); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x1); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x1, ==, 0x1); + /* Default VID group (0) records the delivering irq, output line 2 */ + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_0), ==, 0); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x1); + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x1, ==, 0x0); + + /* IRQ1 steered to VID group 1 -> output line 3 */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x2); + l2vic_set_vid_group(base, 1, 1); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x2); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x2); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x2, ==, 0x2); + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_1), ==, 1); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x2); + + /* IRQ4 steered to VID group 2 -> output line 4 */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x10); + l2vic_set_vid_group(base, 4, 2); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x10); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x10); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x10, ==, 0x10); + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_2), ==, 4); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x10); + + /* IRQ5 steered to VID group 3 -> output line 5 */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x20); + l2vic_set_vid_group(base, 5, 3); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x20); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x20); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x20, ==, 0x20); + g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_3), ==, 5); + + l2vic_write32(base, L2VIC_INT_CLEARn, 0x20); + + /* Restore defaults; the block below reuses IRQ 3-5 without VID steering */ + l2vic_write32(base, L2VIC_INT_GRPn_0, 0); + l2vic_write32(base, L2VIC_INT_TYPEn, 0); + + /* Multiple pending: at most one active at a time */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0xF); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0xF); + l2vic_write32(base, L2VIC_SOFT_INTn, 0xF); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0xF, !=, 0x0); + + /* + * Only one irq becomes active per delivery; each clear unblocks the + * next pending one, so drain until all four have been delivered. + */ + while ((val = l2vic_read32(base, L2VIC_INT_STATUSn)) & 0xF) { + l2vic_write32(base, L2VIC_INT_CLEARn, val & 0xF); + } + + /* Level-triggered sources ignore soft interrupts */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x0); + l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x20); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x20); + + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x20, ==, 0x0); + + /* Same source, now edge-triggered, does fire */ + l2vic_write32(base, L2VIC_INT_TYPEn, 0x20); + l2vic_write32(base, L2VIC_SOFT_INTn, 0x20); + val = l2vic_read32(base, L2VIC_INT_STATUSn); + g_assert_cmpuint(val & 0x20, ==, 0x20); + + l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_CLEARn, 0xFFFFFFFF); + l2vic_write32(base, L2VIC_INT_GRPn_0, 0); + l2vic_write32(base, L2VIC_INT_GRPn_1, 0); + l2vic_write32(base, L2VIC_INT_GRPn_2, 0); + l2vic_write32(base, L2VIC_INT_GRPn_3, 0); +} + +static void test_l2vic_on_machine(gconstpointer data) +{ + const L2VICMachineCfg *mc = data; + g_autofree char *args = g_strdup_printf("-machine %s", mc->machine); + uint64_t base = mc->cfg->l2vic_base; + + qtest_start(args); + + test_l2vic_register_access(base); + test_l2vic_interrupt_enable(base); + test_l2vic_basic_functionality(base); + test_l2vic_irq_outputs(base); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < ARRAY_SIZE(l2vic_machines); i++) { + g_autofree char *path = g_strdup_printf("/l2vic/%s/all-tests", + l2vic_machines[i].machine); + qtest_add_data_func(path, &l2vic_machines[i], test_l2vic_on_machine); + } + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 56ff860e21..a76863c7dd 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -299,7 +299,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \ ['iommu-riscv-test'] : []) + \ (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) -qtests_hexagon = ['boot-serial-test'] +qtests_hexagon = ['boot-serial-test', 'l2vic-test'] qos_test_ss = ss.source_set() qos_test_ss.add( From 240f3620af7048fea14e0e7daf4ffeab8f6bc820 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:19 -0700 Subject: [PATCH 409/860] tests/functional/hexagon: add arch_tests functional test Add the test_int_steering case from hexagon-arch-tests. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-15-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/functional/hexagon/test_arch_tests.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/functional/hexagon/test_arch_tests.py b/tests/functional/hexagon/test_arch_tests.py index 54a1412c59..2bb34f9b8d 100755 --- a/tests/functional/hexagon/test_arch_tests.py +++ b/tests/functional/hexagon/test_arch_tests.py @@ -52,6 +52,12 @@ class ArchTestsUart(QemuSystemTest): """ self.run_uart_test("test_guest_mode") + def test_int_steering(self) -> None: + """Tests interrupt steering via priority-based routing to + specific threads using STID priority and iassignw. + """ + self.run_uart_test("test_int_steering") + if __name__ == "__main__": QemuSystemTest.main() From 954f0cec387e78fcbb6847f42bdd0c6794a5ed6e Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:20 -0700 Subject: [PATCH 410/860] hw/timer: Add QCT QTimer device model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the QCT QTimer generic timer device used by Hexagon DSP systems. Co-authored-by: Damien Hedde Co-authored-by: Tobias Röhmel Co-authored-by: Sid Manning Co-authored-by: Thomas Marceron Co-authored-by: Mahmoud Kamel Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-16-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- MAINTAINERS | 2 + hw/timer/Kconfig | 3 + hw/timer/meson.build | 2 + hw/timer/qct-qtimer.c | 667 ++++++++++++++++++++++++++++++++++ hw/timer/trace-events | 5 + include/hw/timer/qct-qtimer.h | 43 +++ 6 files changed, 722 insertions(+) create mode 100644 hw/timer/qct-qtimer.c create mode 100644 include/hw/timer/qct-qtimer.h diff --git a/MAINTAINERS b/MAINTAINERS index 85fc30503d..b252d2e5de 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -253,6 +253,8 @@ F: target/hexagon/ F: hw/intc/hex-l2vic.c F: include/hw/intc/hex-l2vic.h F: tests/qtest/l2vic-test.c +F: hw/timer/qct-qtimer.c +F: include/hw/timer/qct-qtimer.h X: target/hexagon/idef-parser/ X: target/hexagon/gen_idef_parser_funcs.py F: linux-user/hexagon/ diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index b3d823ce2c..e1b751a54a 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -65,3 +65,6 @@ config STELLARIS_GPTM config AVR_TIMER16 bool + +config HEX_QTIMER + bool diff --git a/hw/timer/meson.build b/hw/timer/meson.build index 201b5d8316..8323efaf46 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -34,3 +34,5 @@ specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_timer.c')) system_ss.add(when: 'CONFIG_SIFIVE_PWM', if_true: files('sifive_pwm.c')) system_ss.add(when: 'CONFIG_AVR_TIMER16', if_true: files('avr_timer16.c')) + +system_ss.add(when: 'CONFIG_HEX_QTIMER', if_true: files('qct-qtimer.c')) diff --git a/hw/timer/qct-qtimer.c b/hw/timer/qct-qtimer.c new file mode 100644 index 0000000000..f628e8d263 --- /dev/null +++ b/hw/timer/qct-qtimer.c @@ -0,0 +1,667 @@ +/* + * Qualcomm QCT QTimer + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/sysbus.h" +#include "hw/timer/qct-qtimer.h" +#include "migration/vmstate.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "trace.h" + +#define QTIMER_MEM_SIZE_BYTES 0x1000 +#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL + +#define QCT_QTIMER_TIMER_FRAME_ELTS (16) +#define QCT_QTIMER_TIMER_VIEW_ELTS (2) + +#define QCT_QTIMER_AC_CNTFRQ (0x000) +#define QCT_QTIMER_AC_CNTSR (0x004) +#define QCT_QTIMER_AC_CNTTID_0 (0x08) +#define QCT_QTIMER_AC_CNTACR_START (0x40) +#define QCT_QTIMER_AC_CNTACR_END (0x5c) +#define QCT_QTIMER_AC_CNTTID_1 (0x108) +#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */ +#define QCT_QTIMER_AC_CNTACR_RWVT (1 << 4) /* R/W of CNTV_* regs */ +#define QCT_QTIMER_AC_CNTACR_RVOFF (1 << 3) /* R/W of CNTVOFF register */ +#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */ +#define QCT_QTIMER_AC_CNTACR_RPVCT (1 << 1) /* R/W of CNTVCT register */ +#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */ +#define QCT_QTIMER_VERSION (0x0fd0) + +#define QCT_QTIMER_CNTPCT_LO (0x000) +#define QCT_QTIMER_CNTPCT_HI (0x004) +#define QCT_QTIMER_CNT_FREQ (0x010) +#define QCT_QTIMER_CNTPL0ACR (0x014) +#define QCT_QTIMER_CNTPL0ACR_PL0CTEN (1 << 9) +#define QCT_QTIMER_CNTPL0ACR_PL0TVEN (1 << 8) +#define QCT_QTIMER_CNTPL0ACR_PL0VCTEN (1 << 1) +#define QCT_QTIMER_CNTPL0ACR_PL0PCTEN (1 << 0) +#define QCT_QTIMER_CNTP_CVAL_LO (0x020) +#define QCT_QTIMER_CNTP_CVAL_HI (0x024) +#define QCT_QTIMER_CNT_MASK 0x00ffffffffffffffULL +#define QCT_QTIMER_CNT_HI_BITS 24 +#define QCT_QTIMER_CNTP_TVAL (0x028) +#define QCT_QTIMER_CNTP_CTL (0x02c) +#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0) +#define QCT_QTIMER_CNTP_CTL_INTEN (1 << 1) +#define QCT_QTIMER_CNTP_CTL_ISTAT (1 << 2) + +OBJECT_DECLARE_SIMPLE_TYPE(QCTQtimerState, QCT_QTIMER) + +typedef struct QCTHextimerState { + QCTQtimerState *qtimer; + QEMUTimer *timer; /* one-shot deadline timer */ + int64_t offset_ns; /* QEMU_CLOCK_VIRTUAL ns at which cntpct == 0 */ + uint64_t cntval; /* 64-bit physical timer compare value */ + uint32_t control; + uint32_t cnt_ctrl; + uint32_t cntpl0acr; + uint32_t int_level; + qemu_irq irq; +} QCTHextimerState; + +struct QCTQtimerState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + MemoryRegion view_iomem; + uint32_t secure; + QCTHextimerState timer[QCT_QTIMER_TIMER_FRAME_ELTS]; + uint32_t freq_hz; + uint32_t nr_frames; + uint32_t nr_views; + uint32_t frame_stride; + uint32_t freq_scale; +}; + +/* + * QTimer version register: + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Major | Minor | Step | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define QCT_QTIMER_VERSION_VALUE 0x20020000 + +static uint32_t qct_qtimer_cnttid(QCTQtimerState *s, unsigned int half) +{ + uint32_t nibble = 0x1 | (s->nr_views > 1 ? 0x4 : 0x0); + uint32_t base = half * 8; + uint32_t cnttid = 0; + unsigned int i; + + for (i = 0; i < 8; i++) { + if (base + i < s->nr_frames) { + cnttid |= nibble << (i * 4); + } + } + return cnttid; +} + +/* Counter value derived on-demand from QEMU_CLOCK_VIRTUAL. */ +static uint64_t hex_timer_now(QCTHextimerState *s) +{ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t scale; + uint64_t scaled_elapsed; + + if (now <= s->offset_ns) { + return 0; + } + scale = MAX(s->qtimer->freq_scale, 1u); + scaled_elapsed = (uint64_t)(now - s->offset_ns) / scale; + return muldiv64(scaled_elapsed, s->qtimer->freq_hz, + NANOSECONDS_PER_SECOND) & + QCT_QTIMER_CNT_MASK; +} + +/* Arm (or disarm) the one-shot deadline timer. */ +static void hex_timer_rearm(QCTHextimerState *s) +{ + uint32_t scale; + uint64_t base_ns; + int64_t deadline_ns; + + if (!(s->control & QCT_QTIMER_CNTP_CTL_ENABLE)) { + timer_del(s->timer); + return; + } + + scale = MAX(s->qtimer->freq_scale, 1u); + /* + * Round the ticks-to-ns conversion up so that hex_timer_now(), which + * truncates when it divides elapsed ns by scale, is guaranteed to + * report >= cntval once this deadline fires. A truncating conversion + * here could re-arm at the same deadline forever when scale > 1. + */ + base_ns = muldiv64_round_up(s->cntval, NANOSECONDS_PER_SECOND, + s->qtimer->freq_hz); + if (base_ns > + ((uint64_t)INT64_MAX - (uint64_t)s->offset_ns) / scale) { + timer_del(s->timer); + return; + } + deadline_ns = s->offset_ns + (int64_t)(base_ns * scale); + timer_mod(s->timer, deadline_ns); +} + +static void hex_timer_update(QCTHextimerState *s) +{ + int level = s->int_level && + (s->control & QCT_QTIMER_CNTP_CTL_ENABLE) && + !(s->control & QCT_QTIMER_CNTP_CTL_INTEN); + + trace_qtimer_interrupt(); + qemu_set_irq(s->irq, level); +} + +/* + * Access-control (AC) region: offsets below 0x1000, gates CNTFRQ/CNTSR/ + * CNTTID/CNTACR per frame plus the shared VERSION register. + */ +static uint64_t qct_qtimer_ac_read(void *opaque, hwaddr offset, unsigned size) +{ + QCTQtimerState *s = opaque; + uint32_t frame; + + switch (offset) { + case QCT_QTIMER_AC_CNTFRQ: + return s->freq_hz; + case QCT_QTIMER_AC_CNTSR: + return s->secure; + case QCT_QTIMER_AC_CNTTID_0: + return qct_qtimer_cnttid(s, 0); + case QCT_QTIMER_AC_CNTTID_1: + return qct_qtimer_cnttid(s, 1); + case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END: + frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 4; + if (frame >= s->nr_frames) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTACR offset 0x%x\n", + __func__, (int)offset); + return 0; + } + return s->timer[frame].cnt_ctrl; + case QCT_QTIMER_VERSION: + return QCT_QTIMER_VERSION_VALUE; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + return 0; + } +} + +static void qct_qtimer_ac_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + QCTQtimerState *s = opaque; + uint32_t frame; + + switch (offset) { + case QCT_QTIMER_AC_CNTFRQ: + if (value == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTFRQ value 0\n", + __func__); + return; + } + s->freq_hz = value; + return; + case QCT_QTIMER_AC_CNTSR: + if (value > 0xff) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTSR value 0x%x\n", + __func__, (int)value); + return; + } + s->secure = value; + return; + case QCT_QTIMER_AC_CNTACR_START ... QCT_QTIMER_AC_CNTACR_END: + frame = (offset - QCT_QTIMER_AC_CNTACR_START) / 4; + if (frame >= s->nr_frames) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad CNTACR offset 0x%x\n", + __func__, (int)offset); + return; + } + s->timer[frame].cnt_ctrl = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + return; + } +} + +static const MemoryRegionOps qct_qtimer_ac_ops = { + .read = qct_qtimer_ac_read, + .write = qct_qtimer_ac_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +/* + * View region: a flat array of (frame, view) slots, each frame_stride + * bytes wide, holding the per-frame CNTPCT/CNTP_CVAL/CNTP_TVAL/CNTP_CTL + * register set. + */ +static QCTHextimerState *qct_qtimer_demux(QCTQtimerState *s, hwaddr offset, + uint32_t *reg_offset, + uint32_t *view) +{ + uint32_t stride = s->frame_stride; + uint32_t stride_shift = ctz32(stride); + uint32_t slot_nr = offset >> stride_shift; + uint32_t frame = slot_nr / s->nr_views; + + *reg_offset = offset & (stride - 1); + *view = slot_nr % s->nr_views; + if (frame >= s->nr_frames) { + return NULL; + } + return &s->timer[frame]; +} + +/* Frames 8+ are described by CNTTID_1; each frame's 2nd view is a gated bit. */ +static bool qct_qtimer_view_visible(QCTQtimerState *s, uint32_t frame, + uint32_t view) +{ + uint32_t cnttid = qct_qtimer_cnttid(s, frame < 8 ? 0 : 1); + uint32_t frame_idx = frame < 8 ? frame : frame - 8; + + return !view || (cnttid & (0x4 << (frame_idx * 4))); +} + +static bool access_ok(QCTHextimerState *s, uint32_t reg_offset, uint32_t view) +{ + uint32_t acr; + uint32_t pl0acr; + + switch (reg_offset) { + case QCT_QTIMER_CNT_FREQ: + acr = QCT_QTIMER_AC_CNTACR_RFRQ; + pl0acr = QCT_QTIMER_CNTPL0ACR_PL0PCTEN | + QCT_QTIMER_CNTPL0ACR_PL0VCTEN; + break; + case QCT_QTIMER_CNTPCT_LO: + case QCT_QTIMER_CNTPCT_HI: + acr = QCT_QTIMER_AC_CNTACR_RPCT; + pl0acr = QCT_QTIMER_CNTPL0ACR_PL0PCTEN; + break; + case QCT_QTIMER_CNTP_CVAL_LO: + case QCT_QTIMER_CNTP_CVAL_HI: + case QCT_QTIMER_CNTP_TVAL: + case QCT_QTIMER_CNTP_CTL: + acr = QCT_QTIMER_AC_CNTACR_RWPT; + pl0acr = QCT_QTIMER_CNTPL0ACR_PL0CTEN; + break; + default: + /* CNTPL0ACR and VERSION are ungated. */ + return true; + } + + if (!(s->cnt_ctrl & acr)) { + return false; + } + return !view || (s->cntpl0acr & pl0acr); +} + +static MemTxResult hex_timer_read(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + QCTQtimerState *qs = opaque; + uint32_t reg_offset; + uint32_t view; + QCTHextimerState *s = qct_qtimer_demux(qs, offset, ®_offset, &view); + uint32_t frame; + + if (!s) { + *data = 0; + return MEMTX_ACCESS_ERROR; + } + frame = s - qs->timer; + + trace_qtimer_read(offset); + + if (!qct_qtimer_view_visible(qs, frame, view)) { + *data = 0; + return MEMTX_OK; + } + + if (!access_ok(s, reg_offset, view)) { + return MEMTX_ACCESS_ERROR; + } + + switch (reg_offset) { + case QCT_QTIMER_CNT_FREQ: + *data = s->qtimer->freq_hz; + return MEMTX_OK; + case QCT_QTIMER_CNTP_CVAL_LO: + *data = extract64(s->cntval, 0, 32); + return MEMTX_OK; + case QCT_QTIMER_CNTP_CVAL_HI: + /* HI half is 24-bit per TRM; bits [31:24] are reserved. */ + *data = extract64(s->cntval, 32, QCT_QTIMER_CNT_HI_BITS); + return MEMTX_OK; + case QCT_QTIMER_CNTPCT_LO: + *data = extract64(hex_timer_now(s), 0, 32); + return MEMTX_OK; + case QCT_QTIMER_CNTPCT_HI: + *data = extract64(hex_timer_now(s), 32, QCT_QTIMER_CNT_HI_BITS); + return MEMTX_OK; + case QCT_QTIMER_CNTP_TVAL: + *data = (uint32_t)(int32_t)(int64_t)(s->cntval - hex_timer_now(s)); + return MEMTX_OK; + case QCT_QTIMER_CNTP_CTL: + /* + * CNTP_CTL: bit 0 EN, bit 1 IMASK, bit 2 ISTAT (interrupt + * pending). ISTAT tracks int_level and is read-only. + */ + *data = s->control | ((s->int_level & 0x1) << 2); + return MEMTX_OK; + case QCT_QTIMER_CNTPL0ACR: + *data = view ? 0 : s->cntpl0acr; + return MEMTX_OK; + case QCT_QTIMER_VERSION: + *data = QCT_QTIMER_VERSION_VALUE; + return MEMTX_OK; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + *data = 0; + return MEMTX_ACCESS_ERROR; + } +} + +static MemTxResult hex_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + QCTQtimerState *qs = opaque; + uint32_t reg_offset; + uint32_t view; + QCTHextimerState *s = qct_qtimer_demux(qs, offset, ®_offset, &view); + uint32_t frame; + + if (!s) { + return MEMTX_ACCESS_ERROR; + } + frame = s - qs->timer; + + trace_qtimer_write(offset, value); + + if (!qct_qtimer_view_visible(qs, frame, view)) { + return MEMTX_OK; + } + + if (!access_ok(s, reg_offset, view)) { + return MEMTX_ACCESS_ERROR; + } + + switch (reg_offset) { + case QCT_QTIMER_CNTP_CVAL_LO: + s->int_level = 0; + s->cntval = deposit64(s->cntval, 0, 32, value); + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTP_CVAL_HI: + s->int_level = 0; + /* HI half is 24-bit per TRM; bits [31:24] are reserved. */ + s->cntval = deposit64(s->cntval, 32, QCT_QTIMER_CNT_HI_BITS, value) & + QCT_QTIMER_CNT_MASK; + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTP_CTL: + /* ISTAT (bit 2) is read-only; keep SW writes from polluting it. */ + s->control = value & ~QCT_QTIMER_CNTP_CTL_ISTAT; + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTP_TVAL: + /* TVAL write: CVAL = CNTPCT + TVAL (TVAL is signed 32-bit). */ + s->int_level = 0; + s->cntval = (hex_timer_now(s) + (int64_t)(int32_t)value) & + QCT_QTIMER_CNT_MASK; + hex_timer_rearm(s); + break; + case QCT_QTIMER_CNTPL0ACR: + if (!view) { + s->cntpl0acr = value; + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad offset 0x%x\n", __func__, + (int)offset); + return MEMTX_ACCESS_ERROR; + } + hex_timer_update(s); + return MEMTX_OK; +} + +static void hex_timer_tick(void *opaque) +{ + QCTHextimerState *s = opaque; + uint64_t now = hex_timer_now(s); + uint64_t diff56 = (now - s->cntval) & QCT_QTIMER_CNT_MASK; + int64_t signed_diff = (int64_t)(diff56 << 8) >> 8; + + if (signed_diff >= 0) { + s->int_level = 1; + hex_timer_update(s); + } else { + hex_timer_rearm(s); + } +} + +static const MemoryRegionOps hex_timer_ops = { + .read_with_attrs = hex_timer_read, + .write_with_attrs = hex_timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + .unaligned = false, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static const VMStateDescription vmstate_qct_hextimer = { + .name = "qct-hextimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(control, QCTHextimerState), + VMSTATE_UINT32(cnt_ctrl, QCTHextimerState), + VMSTATE_INT64(offset_ns, QCTHextimerState), + VMSTATE_UINT64(cntval, QCTHextimerState), + VMSTATE_UINT32(cntpl0acr, QCTHextimerState), + VMSTATE_UINT32(int_level, QCTHextimerState), + VMSTATE_TIMER_PTR(timer, QCTHextimerState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_qct_qtimer = { + .name = "qct-qtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(freq_hz, QCTQtimerState), + VMSTATE_UINT32(secure, QCTQtimerState), + VMSTATE_STRUCT_VARRAY_UINT32(timer, QCTQtimerState, nr_frames, + 1, vmstate_qct_hextimer, QCTHextimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void qct_qtimer_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + QCTQtimerState *s = QCT_QTIMER(dev); + unsigned int i; + + if (s->nr_frames > QCT_QTIMER_TIMER_FRAME_ELTS) { + error_setg(errp, "nr_frames too high"); + return; + } + if (s->nr_views > QCT_QTIMER_TIMER_VIEW_ELTS) { + error_setg(errp, "nr_views too high"); + return; + } + if (s->freq_hz == 0) { + error_setg(errp, "freq-hz must be nonzero"); + return; + } + if (s->frame_stride == 0 || !is_power_of_2(s->frame_stride)) { + error_setg(errp, "frame_stride must be a nonzero power of two"); + return; + } + + memory_region_init_io(&s->iomem, OBJECT(s), &qct_qtimer_ac_ops, s, + "qct-qtimer-ac", QTIMER_MEM_SIZE_BYTES); + sysbus_init_mmio(sbd, &s->iomem); + + memory_region_init_io(&s->view_iomem, OBJECT(s), &hex_timer_ops, s, + "qct-qtimer-view", + (uint64_t)s->frame_stride * s->nr_frames * + s->nr_views); + sysbus_init_mmio(sbd, &s->view_iomem); + + for (i = 0; i < s->nr_frames; i++) { + QCTHextimerState *t = &s->timer[i]; + + t->qtimer = s; + s->secure |= (1 << i); + + sysbus_init_irq(sbd, &t->irq); + t->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hex_timer_tick, t); + } +} + +static void qct_qtimer_unrealize(DeviceState *dev) +{ + QCTQtimerState *s = QCT_QTIMER(dev); + unsigned int i; + + for (i = 0; i < s->nr_frames; i++) { + QCTHextimerState *t = &s->timer[i]; + + if (t->timer) { + timer_free(t->timer); + t->timer = NULL; + } + } +} + +static void qct_qtimer_reset_hold(Object *obj, ResetType type) +{ + QCTQtimerState *s = QCT_QTIMER(obj); + unsigned int i; + + for (i = 0; i < s->nr_frames; i++) { + QCTHextimerState *t = &s->timer[i]; + + /* + * Per TRM: CTL = 0 (EN=0, IMASK=0, ISTAT=0), CVAL = 0 so that + * TVAL (= CVAL - CNTPCT) also reads 0 at reset. The QEMUTimer is + * only armed when SW sets CTL.EN=1, so cntval=0 does not cause a + * spurious fire before SW programs the compare value. + */ + t->control = 0; + t->cnt_ctrl = QCT_QTIMER_AC_CNTACR_RWPT | QCT_QTIMER_AC_CNTACR_RWVT | + QCT_QTIMER_AC_CNTACR_RVOFF | QCT_QTIMER_AC_CNTACR_RFRQ | + QCT_QTIMER_AC_CNTACR_RPVCT | QCT_QTIMER_AC_CNTACR_RPCT; + t->cntval = 0; + t->cntpl0acr = 0; + t->int_level = 0; + t->offset_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + timer_del(t->timer); + qemu_set_irq(t->irq, 0); + } +} + +static const Property qct_qtimer_properties[] = { + DEFINE_PROP_UINT32("freq-hz", QCTQtimerState, freq_hz, + QTIMER_DEFAULT_FREQ_HZ), + DEFINE_PROP_UINT32("freq-scale", QCTQtimerState, freq_scale, 1), + DEFINE_PROP_UINT32("nr_frames", QCTQtimerState, nr_frames, 2), + DEFINE_PROP_UINT32("nr_views", QCTQtimerState, nr_views, 1), + DEFINE_PROP_UINT32("frame_stride", QCTQtimerState, frame_stride, 0x1000), +}; + +static void qct_qtimer_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_props(dc, qct_qtimer_properties); + dc->realize = qct_qtimer_realize; + dc->unrealize = qct_qtimer_unrealize; + dc->vmsd = &vmstate_qct_qtimer; + rc->phases.hold = qct_qtimer_reset_hold; +} + +/* QTimer interface implementation, backing HEX_SREG_TIMERLO/TIMERHI */ +static uint32_t qct_qtimer_get_timer_lo_impl(const QctQtimerInterface *obj) +{ + QCTQtimerState *s = QCT_QTIMER((QctQtimerInterface *)obj); + + return s->nr_frames > 0 ? extract64(hex_timer_now(&s->timer[0]), 0, 32) + : 0; +} + +static uint32_t qct_qtimer_get_timer_hi_impl(const QctQtimerInterface *obj) +{ + QCTQtimerState *s = QCT_QTIMER((QctQtimerInterface *)obj); + + return s->nr_frames > 0 ? extract64(hex_timer_now(&s->timer[0]), 32, 32) + : 0; +} + +static void qct_qtimer_interface_class_init(ObjectClass *klass, + const void *data) +{ + QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_CLASS(klass); + + k->get_timer_lo = qct_qtimer_get_timer_lo_impl; + k->get_timer_hi = qct_qtimer_get_timer_hi_impl; +} + +static const TypeInfo qct_qtimer_types[] = { + { + .name = TYPE_QCT_QTIMER_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(QctQtimerInterfaceClass), + .class_init = qct_qtimer_interface_class_init, + }, + { + .name = TYPE_QCT_QTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(QCTQtimerState), + .class_init = qct_qtimer_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_QCT_QTIMER_INTERFACE }, + { } + }, + }, +}; + +DEFINE_TYPES(qct_qtimer_types) diff --git a/hw/timer/trace-events b/hw/timer/trace-events index 634ba1da27..636310f8ca 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -128,3 +128,8 @@ imx_epit_get_freq(uint32_t freq) "ptimer frequency is %u" imx_epit_read(const char *name, uint32_t value) "(%s) = 0x%08x" imx_epit_write(const char *name, uint64_t value) "(%s, value = 0x%08" PRIx64 ")" imx_epit_cmp(uint32_t sr) "sr was %d" + +# qct-qtimer.c +qtimer_interrupt(void) "qtimer interrupt line updated" +qtimer_read(uint64_t offset) "offset 0x%" PRIx64 +qtimer_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 diff --git a/include/hw/timer/qct-qtimer.h b/include/hw/timer/qct-qtimer.h new file mode 100644 index 0000000000..53d8291472 --- /dev/null +++ b/include/hw/timer/qct-qtimer.h @@ -0,0 +1,43 @@ +/* + * Qualcomm QCT QTimer + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_TIMER_QCT_QTIMER_H +#define HW_TIMER_QCT_QTIMER_H + +#include "qom/object.h" + +#define TYPE_QCT_QTIMER "qct-qtimer" + +/* QTimer interface for external access from hexagon_globalreg */ +#define TYPE_QCT_QTIMER_INTERFACE "qct-qtimer-if" + +typedef struct QctQtimerInterface QctQtimerInterface; + +typedef struct QctQtimerInterfaceClass { + InterfaceClass parent_class; + + /* Read the live physical counter, backing HEX_SREG_TIMERLO/TIMERHI */ + uint32_t (*get_timer_lo)(const QctQtimerInterface *qtimer); + uint32_t (*get_timer_hi)(const QctQtimerInterface *qtimer); +} QctQtimerInterfaceClass; + +DECLARE_OBJ_CHECKERS(QctQtimerInterface, QctQtimerInterfaceClass, + QCT_QTIMER_INTERFACE, TYPE_QCT_QTIMER_INTERFACE); + +static inline uint32_t qct_qtimer_get_timer_lo(const QctQtimerInterface *qtimer) +{ + QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_GET_CLASS(qtimer); + return k->get_timer_lo(qtimer); +} + +static inline uint32_t qct_qtimer_get_timer_hi(const QctQtimerInterface *qtimer) +{ + QctQtimerInterfaceClass *k = QCT_QTIMER_INTERFACE_GET_CLASS(qtimer); + return k->get_timer_hi(qtimer); +} + +#endif /* HW_TIMER_QCT_QTIMER_H */ From 57eaab1e8c8d7c1624591b1aeb668f14c3a8131d Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:21 -0700 Subject: [PATCH 411/860] hw/hexagon: connect qtimer device Add the QTimer to the shared hex-subsys so both machine models pick it up. Map its view region, wire its interrupt lines into l2vic, and link it to the globalreg device backing HEX_SREG_TIMERLO/TIMERHI. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-17-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- hw/hexagon/Kconfig | 1 + hw/hexagon/hex-subsys.c | 28 ++++++++++++++++++++++++++ hw/hexagon/hexagon_globalreg.c | 17 ++++++++++++++++ include/hw/hexagon/hexagon.h | 1 + include/hw/hexagon/hexagon_globalreg.h | 4 ++++ 5 files changed, 51 insertions(+) diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig index c75090c44a..83b2763d1e 100644 --- a/hw/hexagon/Kconfig +++ b/hw/hexagon/Kconfig @@ -4,6 +4,7 @@ config HEX_DSP depends on HEXAGON select CPU_CLUSTER select HEX_L2VIC + select HEX_QTIMER config HEX_VIRT bool diff --git a/hw/hexagon/hex-subsys.c b/hw/hexagon/hex-subsys.c index 25c028f657..4e3a418340 100644 --- a/hw/hexagon/hex-subsys.c +++ b/hw/hexagon/hex-subsys.c @@ -11,6 +11,7 @@ #include "hw/hexagon/hexagon_globalreg.h" #include "hw/hexagon/hexagon_tlb.h" #include "hw/intc/hex-l2vic.h" +#include "hw/timer/qct-qtimer.h" #include "hw/cpu/cluster.h" #include "hw/core/loader.h" #include "hw/core/qdev-properties.h" @@ -20,6 +21,11 @@ #define HEX_L2VIC_CPU_IRQS 8 +/* Number of QTimer frames instantiated for every Hexagon machine. */ +#define HEX_QTIMER_NR_FRAMES 3 + +#define HEX_QTIMER_L2VIC_IRQ_BASE 2 + static DeviceState *l2vic_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg) { @@ -43,6 +49,25 @@ static void l2vic_connect_cpu(DeviceState *l2vic, DeviceState *cpu) } } +static DeviceState *qtimer_create(HexagonCommonMachineState *hms, + const struct hexagon_machine_config *m_cfg) +{ + DeviceState *qtimer = qdev_new(TYPE_QCT_QTIMER); + + object_property_add_child(OBJECT(hms), "qtimer", OBJECT(qtimer)); + qdev_prop_set_uint32(qtimer, "nr_frames", HEX_QTIMER_NR_FRAMES); + sysbus_realize_and_unref(SYS_BUS_DEVICE(qtimer), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 0, m_cfg->csr_base); + sysbus_mmio_map(SYS_BUS_DEVICE(qtimer), 1, m_cfg->qtmr_region); + for (unsigned int i = 0; i < HEX_QTIMER_NR_FRAMES; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(qtimer), i, + qdev_get_gpio_in(hms->l2vic, + HEX_QTIMER_L2VIC_IRQ_BASE + i)); + } + + return qtimer; +} + static DeviceState *globalreg_create(HexagonCommonMachineState *hms, const struct hexagon_machine_config *m_cfg, Rev_t rev) @@ -54,6 +79,8 @@ static DeviceState *globalreg_create(HexagonCommonMachineState *hms, qdev_prop_set_uint32(glob_regs, "dsp-rev", rev); object_property_set_link(OBJECT(glob_regs), "l2vic", OBJECT(hms->l2vic), &error_fatal); + object_property_set_link(OBJECT(glob_regs), "qtimer", OBJECT(hms->qtimer), + &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(glob_regs), &error_fatal); return glob_regs; @@ -110,6 +137,7 @@ void hex_subsys_create(HexagonCommonMachineState *hms, hms->cluster = cluster_create(hms); hms->l2vic = l2vic_create(hms, m_cfg); + hms->qtimer = qtimer_create(hms, m_cfg); hms->glob_regs = globalreg_create(hms, m_cfg, rev); hms->tlb = tlb_create(hms, m_cfg); } diff --git a/hw/hexagon/hexagon_globalreg.c b/hw/hexagon/hexagon_globalreg.c index 285cb48c44..61621cf2b8 100644 --- a/hw/hexagon/hexagon_globalreg.c +++ b/hw/hexagon/hexagon_globalreg.c @@ -12,6 +12,7 @@ #include "hw/core/sysbus.h" #include "hw/core/resettable.h" #include "hw/intc/hex-l2vic.h" +#include "hw/timer/qct-qtimer.h" #include "migration/vmstate.h" #include "qom/object.h" #include "target/hexagon/cpu.h" @@ -141,11 +142,21 @@ static inline bool is_vid_reg(uint32_t reg) return reg == HEX_SREG_VID || reg == HEX_SREG_VID1; } +static inline bool is_timer_reg(uint32_t reg) +{ + return reg == HEX_SREG_TIMERLO || reg == HEX_SREG_TIMERHI; +} + static uint32_t get_reg_value(HexagonGlobalRegState *s, uint32_t reg) { if (is_vid_reg(reg)) { return l2vic_read_vid(s->l2vic, reg == HEX_SREG_VID ? 0 : 1); } + if (is_timer_reg(reg)) { + return reg == HEX_SREG_TIMERLO ? + qct_qtimer_get_timer_lo(s->qtimer) : + qct_qtimer_get_timer_hi(s->qtimer); + } return s->regs[reg]; } @@ -289,6 +300,10 @@ static void hexagon_globalreg_realize(DeviceState *dev, Error **errp) error_setg(errp, "hexagon_globalreg: 'l2vic' link property not set"); return; } + if (!s->qtimer) { + error_setg(errp, "hexagon_globalreg: 'qtimer' link property not set"); + return; + } } static const VMStateDescription vmstate_hexagon_globalreg = { @@ -312,6 +327,8 @@ static const VMStateDescription vmstate_hexagon_globalreg = { static const Property hexagon_globalreg_properties[] = { DEFINE_PROP_LINK("l2vic", HexagonGlobalRegState, l2vic, TYPE_HEX_L2VIC_INTERFACE, HexL2VicInterface *), + DEFINE_PROP_LINK("qtimer", HexagonGlobalRegState, qtimer, + TYPE_QCT_QTIMER_INTERFACE, QctQtimerInterface *), DEFINE_PROP_UINT32("boot-evb", HexagonGlobalRegState, boot_evb, 0x0), DEFINE_PROP_UINT64("config-table-addr", HexagonGlobalRegState, config_table_addr, 0xffffffffULL), diff --git a/include/hw/hexagon/hexagon.h b/include/hw/hexagon/hexagon.h index ec1578807d..3d7b3cb12d 100644 --- a/include/hw/hexagon/hexagon.h +++ b/include/hw/hexagon/hexagon.h @@ -159,6 +159,7 @@ struct HexagonCommonMachineState { MemoryRegion vtcm; DeviceState *cluster; DeviceState *l2vic; + DeviceState *qtimer; DeviceState *glob_regs; DeviceState *tlb; }; diff --git a/include/hw/hexagon/hexagon_globalreg.h b/include/hw/hexagon/hexagon_globalreg.h index 397dc6854c..07437dfabb 100644 --- a/include/hw/hexagon/hexagon_globalreg.h +++ b/include/hw/hexagon/hexagon_globalreg.h @@ -11,6 +11,7 @@ #include "hw/core/qdev.h" #include "hw/core/sysbus.h" #include "hw/intc/hex-l2vic.h" +#include "hw/timer/qct-qtimer.h" #include "qom/object.h" #include "target/hexagon/cpu.h" @@ -26,6 +27,9 @@ struct HexagonGlobalRegState { /* L2VIC interface used to back the VID/VID1 registers */ HexL2VicInterface *l2vic; + /* QTimer interface used to back the TIMERLO/TIMERHI registers */ + QctQtimerInterface *qtimer; + /* Global performance cycle counter base */ uint64_t g_pcycle_base; From 71d618d3aa64bd7a33d8fc14fff6f3051ad633c2 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:22 -0700 Subject: [PATCH 412/860] tests/qtest: add qct-qtimer qtest Add a qtest exercising the QCT QTimer's register access, view-region frame addressing, and one-shot timer firing behavior. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-18-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- MAINTAINERS | 1 + tests/qtest/meson.build | 2 +- tests/qtest/qct-qtimer-test.c | 385 ++++++++++++++++++++++++++++++++++ 3 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/qct-qtimer-test.c diff --git a/MAINTAINERS b/MAINTAINERS index b252d2e5de..8db47c48fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -255,6 +255,7 @@ F: include/hw/intc/hex-l2vic.h F: tests/qtest/l2vic-test.c F: hw/timer/qct-qtimer.c F: include/hw/timer/qct-qtimer.h +F: tests/qtest/qct-qtimer-test.c X: target/hexagon/idef-parser/ X: target/hexagon/gen_idef_parser_funcs.py F: linux-user/hexagon/ diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index a76863c7dd..651246b2eb 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -299,7 +299,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \ ['iommu-riscv-test'] : []) + \ (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) -qtests_hexagon = ['boot-serial-test', 'l2vic-test'] +qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-test'] qos_test_ss = ss.source_set() qos_test_ss.add( diff --git a/tests/qtest/qct-qtimer-test.c b/tests/qtest/qct-qtimer-test.c new file mode 100644 index 0000000000..fd8bb0af77 --- /dev/null +++ b/tests/qtest/qct-qtimer-test.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QTest testcase for the QCT QTimer + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "hw/hexagon/hexagon.h" +#include "qemu/bitops.h" + +#include "hw/hexagon/machine_cfg_v68n_1024.h.inc" +#include "hw/hexagon/machine_cfg_v66g_1024.h.inc" + +#define QTIMER_DEFAULT_FREQ_HZ 19200000ULL + +#define QCT_QTIMER_CNTPCT_LO (0x000) +#define QCT_QTIMER_CNTPCT_HI (0x004) +#define QCT_QTIMER_CNT_FREQ (0x010) +#define QCT_QTIMER_CNTP_CVAL_LO (0x020) +#define QCT_QTIMER_CNTP_TVAL (0x028) +#define QCT_QTIMER_CNTP_CTL (0x02c) +#define QCT_QTIMER_CNTP_CTL_ENABLE (1 << 0) + +#define QTIMER_FRAME_STRIDE 0x1000 +/* Frames instantiated by hex-subsys, and the L2VIC input frame 0 drives. */ +#define QTIMER_NR_FRAMES 3 +#define QTIMER_L2VIC_IRQ_BASE 2 + +#define QCT_QTIMER_AC_CNTFRQ (0x000) +#define QCT_QTIMER_AC_CNTSR (0x004) +#define QCT_QTIMER_AC_CNTTID_0 (0x08) +#define QCT_QTIMER_AC_CNTACR_START (0x40) +#define QCT_QTIMER_AC_CNTACR_ALL (0x3f) +#define QCT_QTIMER_AC_CNTACR_RWPT (1 << 5) /* R/W of CNTP_* regs */ +#define QCT_QTIMER_AC_CNTACR_RFRQ (1 << 2) /* R/W of CNTFRQ register */ +#define QCT_QTIMER_AC_CNTACR_RPCT (1 << 0) /* R/W of CNTPCT register */ + +static uint64_t qtimer_view_base; +static uint64_t qtimer_ac_base; + +#define TIMER_TEST_OFFSET 1000 +/* TIMER_TEST_OFFSET ticks expressed in nanoseconds of QEMU_CLOCK_VIRTUAL */ +#define TIMER_TEST_NS \ + ((TIMER_TEST_OFFSET * 1000000000ULL) / QTIMER_DEFAULT_FREQ_HZ) + +static uint32_t qtimer_read32(uint64_t base, uint32_t offset) +{ + return readl(base + offset); +} + +static void qtimer_write32(uint64_t base, uint32_t offset, uint32_t value) +{ + writel(base + offset, value); +} + +static uint64_t qtimer_read64(uint64_t base, uint32_t offset) +{ + uint32_t lo = qtimer_read32(base, offset); + uint32_t hi = qtimer_read32(base, offset + 4); + + return ((uint64_t)hi << 32) | lo; +} + +static void qtimer_write64(uint64_t base, uint32_t offset, uint64_t value) +{ + qtimer_write32(base, offset, extract64(value, 0, 32)); + qtimer_write32(base, offset + 4, extract64(value, 32, 32)); +} + +static void test_qtimer_basic_access(void) +{ + uint32_t val; + + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); +} + +static void test_qtimer_multiple_frames(void) +{ + uint32_t val; + uint64_t frame0_base = qtimer_view_base; + uint64_t frame1_base = qtimer_view_base + 0x1000; + + val = qtimer_read32(frame0_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); + + val = qtimer_read32(frame1_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); +} + +static void test_qtimer_register_reads(void) +{ + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL); +} + +static void test_qtimer_control_registers(void) +{ + uint32_t ctl_val; + uint64_t cval_before, cval_after; + + cval_before = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL, 1000); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1); + ctl_val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(ctl_val & 1, ==, 1); + + /* CVAL should be greater than before since we set TVAL */ + cval_after = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(cval_after, >, cval_before); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 0); + ctl_val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(ctl_val & 1, ==, 0); +} + +static void test_qtimer_cval_access(void) +{ + uint64_t current_time, test_cval, read_cval; + + current_time = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + test_cval = current_time + 10000; + + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, test_cval); + read_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(read_cval, ==, test_cval); +} + +static void test_qtimer_counter_progression(void) +{ + uint32_t freq; + uint64_t count1, count2; + + /* + * In qtest mode the virtual clock does not advance on its own, so + * reading the counter twice must give the same value. + */ + count1 = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + count2 = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(count2, ==, count1); + + freq = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ); +} + +static void test_qtimer_timer_behavior(void) +{ + uint64_t current_count, target_count, read_cval, new_count; + uint64_t ctl_val, count_after_disable; + + current_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + + target_count = current_count + TIMER_TEST_OFFSET; + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, target_count); + + read_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(read_cval, ==, target_count); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1); + + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + /* EN set, IMASK clear, ISTAT not yet pending */ + g_assert_cmpuint(ctl_val, ==, 0x1); + + /* Step forward but not past the target */ + qtest_clock_step(global_qtest, TIMER_TEST_NS / 2); + new_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(new_count, >=, current_count); + + /* Step past the target */ + qtest_clock_step(global_qtest, TIMER_TEST_NS); + new_count = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(new_count, >=, target_count); + + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 0); + + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + /* EN cleared, ISTAT set since new_count >= target_count */ + g_assert_cmpuint(ctl_val, ==, 0x4); + + /* + * CNTPCT runs independently of CNTP_CTL.EN: only the compare/IRQ + * logic is gated by EN, so the counter must keep advancing. + */ + qtest_clock_step(global_qtest, TIMER_TEST_NS / 2); + count_after_disable = qtimer_read64(qtimer_view_base, + QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(count_after_disable, >, new_count); + + /* ISTAT remains set while CNTPCT >= CVAL, even with EN=0 */ + ctl_val = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(ctl_val, ==, 0x4); +} + +/* Test the access-control region: CNTFRQ, CNTSR, CNTTID_0, CNTACR frame 0 */ +static void test_qtimer_ac_region(void) +{ + uint32_t freq, sr, tid0, acr0; + + freq = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ); + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ); + + /* A write of 0 to CNTFRQ must be ignored (freq-hz must stay nonzero). */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ, 0); + freq = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTFRQ); + g_assert_cmpuint(freq, ==, QTIMER_DEFAULT_FREQ_HZ); + + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTSR, 0x3); + sr = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTSR); + g_assert_cmpuint(sr, ==, 0x3); + + tid0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTTID_0); + g_assert_cmpuint(tid0, ==, 0x111); + + /* CNTACR for frame 0 defaults to full read/write permissions. */ + acr0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START); + g_assert_cmpuint(acr0, !=, 0); + + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, 0); + acr0 = qtimer_read32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START); + g_assert_cmpuint(acr0, ==, 0); + + /* Restore full permissions so later view-region tests keep working. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + QCT_QTIMER_AC_CNTACR_ALL); +} + +static void test_qtimer_access_denied(void) +{ + uint32_t acr_all = QCT_QTIMER_AC_CNTACR_ALL; + uint64_t cval, saved_cval; + uint32_t val; + + /* Park CVAL at a known nonzero value while access is still permitted. */ + saved_cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTPCT_LO) + 10000; + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, saved_cval); + g_assert_cmpuint(qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO), + ==, saved_cval); + + /* Clearing RFRQ denies CNTFRQ reads. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + acr_all & ~QCT_QTIMER_AC_CNTACR_RFRQ); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, 0); + + /* Clearing RPCT denies CNTPCT reads, both halves. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + acr_all & ~QCT_QTIMER_AC_CNTACR_RPCT); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTPCT_LO); + g_assert_cmpuint(val, ==, 0); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTPCT_HI); + g_assert_cmpuint(val, ==, 0); + + /* + * Clearing RWPT denies the whole CNTP_* set: CVAL/TVAL/CTL reads all + * read back 0 even though CVAL holds saved_cval. + */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, + acr_all & ~QCT_QTIMER_AC_CNTACR_RWPT); + cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(cval, ==, 0); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL); + g_assert_cmpuint(val, ==, 0); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(val, ==, 0); + + /* Denied writes must be dropped rather than applied. */ + qtimer_write64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO, 0x1234); + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_TVAL, 0x5678); + qtimer_write32(qtimer_view_base, QCT_QTIMER_CNTP_CTL, 1); + + /* Restoring RWPT reveals that CVAL still holds the pre-denial value. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, acr_all); + cval = qtimer_read64(qtimer_view_base, QCT_QTIMER_CNTP_CVAL_LO); + g_assert_cmpuint(cval, ==, saved_cval); + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNTP_CTL); + g_assert_cmpuint(val & 1, ==, 0); + + /* CNTFRQ and CNTPCT work again once permissions are back. */ + val = qtimer_read32(qtimer_view_base, QCT_QTIMER_CNT_FREQ); + g_assert_cmpuint(val, ==, QTIMER_DEFAULT_FREQ_HZ); + + /* + * An unimplemented offset in the view region is also an access error, + * and reads back as 0. + */ + val = qtimer_read32(qtimer_view_base, 0x100); + g_assert_cmpuint(val, ==, 0); + + /* Leave the frame with full permissions for any later test. */ + qtimer_write32(qtimer_ac_base, QCT_QTIMER_AC_CNTACR_START, acr_all); +} + +static void test_qtimer_frame_irq_routing(void) +{ + unsigned int frame, other; + uint64_t frame_base; + + qtest_irq_intercept_in(global_qtest, "/machine/l2vic"); + + for (frame = 0; frame < QTIMER_NR_FRAMES; frame++) { + frame_base = qtimer_view_base + frame * QTIMER_FRAME_STRIDE; + + qtimer_write32(frame_base, QCT_QTIMER_CNTP_TVAL, TIMER_TEST_OFFSET); + qtimer_write32(frame_base, QCT_QTIMER_CNTP_CTL, + QCT_QTIMER_CNTP_CTL_ENABLE); + g_assert_false(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + frame)); + + /* Step past the deadline so the frame raises its interrupt. */ + qtest_clock_step(global_qtest, TIMER_TEST_NS * 2); + g_assert_true(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + frame)); + + /* No other frame's line may be disturbed. */ + for (other = 0; other < QTIMER_NR_FRAMES; other++) { + if (other != frame) { + g_assert_false(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + other)); + } + } + + /* Clearing EN drops the interrupt, leaving a clean slate. */ + qtimer_write32(frame_base, QCT_QTIMER_CNTP_CTL, 0); + g_assert_false(qtest_get_irq(global_qtest, + QTIMER_L2VIC_IRQ_BASE + frame)); + } +} + +typedef struct { + const char *machine; + const struct hexagon_machine_config *cfg; +} QtimerMachineCfg; + +static const QtimerMachineCfg qtimer_machines[] = { + { "virt", &v68n_1024 }, + { "V66G_1024", &v66g_1024 }, +}; + +static void test_qtimer_on_machine(gconstpointer data) +{ + const QtimerMachineCfg *mc = data; + g_autofree char *args = g_strdup_printf( + "-machine %s -global qct-qtimer.freq-scale=1", mc->machine); + + qtimer_view_base = mc->cfg->qtmr_region; + qtimer_ac_base = mc->cfg->csr_base; + + qtest_start(args); + + test_qtimer_basic_access(); + test_qtimer_multiple_frames(); + test_qtimer_register_reads(); + test_qtimer_control_registers(); + test_qtimer_cval_access(); + test_qtimer_counter_progression(); + test_qtimer_timer_behavior(); + test_qtimer_ac_region(); + test_qtimer_access_denied(); + test_qtimer_frame_irq_routing(); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + size_t i; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < ARRAY_SIZE(qtimer_machines); i++) { + g_autofree char *path = g_strdup_printf("/qct-qtimer/%s/all-tests", + qtimer_machines[i].machine); + qtest_add_data_func(path, &qtimer_machines[i], test_qtimer_on_machine); + } + + return g_test_run(); +} From 701eb2ac98ba543a3a458a7fcaf8cc0f5e469ed4 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Wed, 5 Aug 2026 21:27:23 -0700 Subject: [PATCH 413/860] tests/functional/hexagon: enable more arch_tests cases Add more tests from hexagon-arch-tests, enabled by QTimer device. These exercise cache maintenance ops, l2vic, thread start/stop, tlb/mmu operations, and user-mode transitions. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260806042723.3785369-19-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/functional/hexagon/test_arch_tests.py | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/functional/hexagon/test_arch_tests.py b/tests/functional/hexagon/test_arch_tests.py index 2bb34f9b8d..0834398c3b 100755 --- a/tests/functional/hexagon/test_arch_tests.py +++ b/tests/functional/hexagon/test_arch_tests.py @@ -58,6 +58,38 @@ class ArchTestsUart(QemuSystemTest): """ self.run_uart_test("test_int_steering") + def test_cache(self) -> None: + """Tests cache operations: dckill/ickill, l2kill, dczeroa, + dccleaninva, cache disable/enable, barriers, and dcinva/dccleana. + """ + self.run_uart_test("test_cache") + + def test_l2vic(self) -> None: + """Tests the L2VIC interrupt controller: enable readback, + interrupt type readback, VID capture, and the fast interface. + """ + self.run_uart_test("test_l2vic") + + def test_threads(self) -> None: + """Tests hardware thread management: start/stop, MODECTL state, + per-thread HTID, shared memory, wait/resume, STID priority, and + SCHEDCFG/BESTWAIT readback. + """ + self.run_uart_test("test_threads") + + def test_tlb_mmu(self) -> None: + """Tests TLB/MMU operations: write/read/probe/invalidate, + global entries, multiple entries, overwrite, ASID matching, + and permission checks. + """ + self.run_uart_test("test_tlb_mmu") + + def test_user_mode(self) -> None: + """Tests user mode / privilege transitions: supervisor mode, + SSR UM/IE/XE/CE/PE bits, and the trap0 user-mode exit handler. + """ + self.run_uart_test("test_user_mode") + if __name__ == "__main__": QemuSystemTest.main() From 9e96b5953e2fb619a5ad6274ebba3baee0b933c4 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 7 Aug 2026 08:22:35 -0700 Subject: [PATCH 414/860] host-utils: fix ssub32/64_saturate return type and clamp direction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ssub32_saturate() and ssub64_saturate() were declared to return bool instead of int32_t/int64_t, and clamped to the wrong bound on overflow. Reviewed-by: Philippe Mathieu-Daudé Cc: qemu-stable@nongnu.org Fixes: 16495533131 ("host-utils: Introduce signed saturation primitives") Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260807152241.1576334-2-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- include/qemu/host-utils.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 2e8da7fb3d..1db9dbb138 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -607,10 +607,10 @@ static inline bool umul64_overflow(uint64_t x, uint64_t y, uint64_t *ret) } /** - * sadd32_saturate - addition with saturation + * sadd32_saturate - 32-bit signed addition with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x + @y, and saturates rather than truncating the result. */ static inline int32_t sadd32_saturate(int32_t x, int32_t y) { @@ -622,10 +622,10 @@ static inline int32_t sadd32_saturate(int32_t x, int32_t y) } /** - * sadd64_saturate - addition with saturation + * sadd64_saturate - 64-bit signed addition with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x + @y, and saturates rather than truncating the result. */ static inline int64_t sadd64_saturate(int64_t x, int64_t y) { @@ -637,31 +637,31 @@ static inline int64_t sadd64_saturate(int64_t x, int64_t y) } /** - * ssub32_saturate - subtraction with saturation + * ssub32_saturate - 32-bit signed subtraction with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x - @y, and saturates rather than truncating the result. */ -static inline bool ssub32_saturate(int32_t x, int32_t y) +static inline int32_t ssub32_saturate(int32_t x, int32_t y) { int32_t ret; if (ssub32_overflow(x, y, &ret)) { - ret = x < 0 ? INT32_MAX : INT32_MIN; + ret = x < 0 ? INT32_MIN : INT32_MAX; } return ret; } /** - * ssub64_saturate - subtraction with saturation + * ssub64_saturate - 64-bit signed subtraction with saturation * @x, @y: addends * - * Computes @x + @y, and saturates rathern than truncating the result. + * Computes @x - @y, and saturates rather than truncating the result. */ -static inline bool ssub64_saturate(int64_t x, int64_t y) +static inline int64_t ssub64_saturate(int64_t x, int64_t y) { int64_t ret; if (ssub64_overflow(x, y, &ret)) { - ret = x < 0 ? INT64_MAX : INT64_MIN; + ret = x < 0 ? INT64_MIN : INT64_MAX; } return ret; } From 8c53023a26ec397fadaf3fd10404df1be57d2816 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 7 Aug 2026 08:22:36 -0700 Subject: [PATCH 415/860] target/hexagon: add GVec overrides for HVX saturating add/sub Reviewed-by: Marco Liebel Link: https://lore.kernel.org/qemu-devel/20260807152241.1576334-3-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/gen_tcg_hvx.h | 87 ++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 0da64d467e..289ce89959 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -234,6 +234,93 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_sub(MO_32, VddV_off, VuuV_off, VvvV_off, \ sizeof(MMVector) * 2, sizeof(MMVector) * 2) +#define fGEN_TCG_V6_vaddbsat(SHORTCODE) \ + tcg_gen_gvec_ssadd(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddhsat(SHORTCODE) \ + tcg_gen_gvec_ssadd(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddwsat(SHORTCODE) \ + tcg_gen_gvec_ssadd(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddubsat(SHORTCODE) \ + tcg_gen_gvec_usadd(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vadduhsat(SHORTCODE) \ + tcg_gen_gvec_usadd(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vadduwsat(SHORTCODE) \ + tcg_gen_gvec_usadd(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_PAIR_ADDSUB(OP, VECE, DST, SRC_A, SRC_B) \ + tcg_gen_gvec_##OP(VECE, DST, SRC_A, SRC_B, \ + 2 * VECTOR_SIZE_BYTE, 2 * VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vaddbsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ssadd, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vaddhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ssadd, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vaddwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ssadd, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vaddubsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(usadd, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vadduhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(usadd, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vadduwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(usadd, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubbsat(SHORTCODE) \ + tcg_gen_gvec_sssub(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubhsat(SHORTCODE) \ + tcg_gen_gvec_sssub(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubwsat(SHORTCODE) \ + tcg_gen_gvec_sssub(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsububsat(SHORTCODE) \ + tcg_gen_gvec_ussub(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubuhsat(SHORTCODE) \ + tcg_gen_gvec_ussub(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubuwsat(SHORTCODE) \ + tcg_gen_gvec_ussub(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vsubbsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(sssub, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(sssub, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(sssub, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsububsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ussub, MO_8, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubuhsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ussub, MO_16, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vsubuwsat_dv(SHORTCODE) \ + fGEN_TCG_PAIR_ADDSUB(ussub, MO_32, VddV_off, VuuV_off, VvvV_off) /* Vector shift right - various forms */ #define fGEN_TCG_V6_vasrh(SHORTCODE) \ do { \ From d47d794ea2c7b3f5baa8d57fbe496c755119e0da Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 7 Aug 2026 08:22:37 -0700 Subject: [PATCH 416/860] target/hexagon: add GVec override for HVX vmpyih multiply Reviewed-by: Marco Liebel Link: https://lore.kernel.org/qemu-devel/20260807152241.1576334-4-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/gen_tcg_hvx.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 289ce89959..7e5372a764 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -321,6 +321,11 @@ static inline void assert_vhist_tmp(DisasContext *ctx) #define fGEN_TCG_V6_vsubuwsat_dv(SHORTCODE) \ fGEN_TCG_PAIR_ADDSUB(ussub, MO_32, VddV_off, VuuV_off, VvvV_off) + +#define fGEN_TCG_V6_vmpyih(SHORTCODE) \ + tcg_gen_gvec_mul(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + /* Vector shift right - various forms */ #define fGEN_TCG_V6_vasrh(SHORTCODE) \ do { \ From 234d8cb6f2e95cc5078b232943e47bc4f56170c3 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 7 Aug 2026 08:22:38 -0700 Subject: [PATCH 417/860] target/hexagon: add GVec overrides for HVX absolute difference Reviewed-by: Marco Liebel Link: https://lore.kernel.org/qemu-devel/20260807152241.1576334-5-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/gen_tcg_hvx.h | 16 +++++++++ target/hexagon/genptr.c | 70 ++++++++++++++++++++++++++++++++++++ target/hexagon/genptr.h | 4 +++ target/hexagon/helper.h | 5 +++ target/hexagon/op_helper.c | 21 +++++++++++ 5 files changed, 116 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index 7e5372a764..cbf84a8cae 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -326,6 +326,22 @@ static inline void assert_vhist_tmp(DisasContext *ctx) tcg_gen_gvec_mul(MO_16, VdV_off, VuV_off, VvV_off, \ VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) +#define fGEN_TCG_V6_vabsdiffub(SHORTCODE) \ + gen_gvec_uabsdiff(MO_8, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vabsdiffuh(SHORTCODE) \ + gen_gvec_uabsdiff(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vabsdiffh(SHORTCODE) \ + gen_gvec_sabsdiff(MO_16, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + +#define fGEN_TCG_V6_vabsdiffw(SHORTCODE) \ + gen_gvec_sabsdiff(MO_32, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) + /* Vector shift right - various forms */ #define fGEN_TCG_V6_vasrh(SHORTCODE) \ do { \ diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 12813f48bf..2a98b13b71 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -37,6 +37,76 @@ #include "genptr.h" +static void gen_sabsdiff_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_sub_i32(t, a, b); + tcg_gen_sub_i32(d, b, a); + tcg_gen_movcond_i32(TCG_COND_LT, d, a, b, d, t); +} + +static void gen_sabsdiff_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_smin_vec(vece, t, a, b); + tcg_gen_smax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_sabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_smin_vec, INDEX_op_smax_vec, 0 + }; + static const GVecGen3 ops[4] = { + [MO_16] = { .fniv = gen_sabsdiff_vec, + .fno = gen_helper_gvec_sabsdiff_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + [MO_32] = { .fni4 = gen_sabsdiff_i32, + .fniv = gen_sabsdiff_vec, + .fno = gen_helper_gvec_sabsdiff_w, + .opt_opc = vecop_list, + .vece = MO_32 }, + }; + + tcg_debug_assert(vece == MO_16 || vece == MO_32); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &ops[vece]); +} + +static void gen_uabsdiff_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_umin_vec(vece, t, a, b); + tcg_gen_umax_vec(vece, d, a, b); + tcg_gen_sub_vec(vece, d, d, t); +} + +void gen_gvec_uabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const TCGOpcode vecop_list[] = { + INDEX_op_sub_vec, INDEX_op_umin_vec, INDEX_op_umax_vec, 0 + }; + static const GVecGen3 ops[4] = { + [MO_8] = { .fniv = gen_uabsdiff_vec, + .fno = gen_helper_gvec_uabsdiff_b, + .opt_opc = vecop_list, + .vece = MO_8 }, + [MO_16] = { .fniv = gen_uabsdiff_vec, + .fno = gen_helper_gvec_uabsdiff_h, + .opt_opc = vecop_list, + .vece = MO_16 }, + }; + + tcg_debug_assert(vece == MO_8 || vece == MO_16); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &ops[vece]); +} + TCGv gen_read_reg(TCGv result, int num) { tcg_gen_mov_tl(result, hex_gpr[num]); diff --git a/target/hexagon/genptr.h b/target/hexagon/genptr.h index 5d09677410..f498398367 100644 --- a/target/hexagon/genptr.h +++ b/target/hexagon/genptr.h @@ -59,6 +59,10 @@ TCGv gen_get_half(TCGv result, int N, TCGv src, bool sign); void gen_set_half(int N, TCGv result, TCGv src); void gen_set_half_i64(int N, TCGv_i64 result, TCGv src); void probe_noshuf_load(TCGv va, int s, int mi); +void gen_gvec_sabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void gen_gvec_uabsdiff(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); extern const target_ulong reg_immut_masks[TOTAL_PER_THREAD_REGS]; diff --git a/target/hexagon/helper.h b/target/hexagon/helper.h index 033e5619d6..78dc28ca9e 100644 --- a/target/hexagon/helper.h +++ b/target/hexagon/helper.h @@ -108,6 +108,11 @@ DEF_HELPER_2(probe_pkt_scalar_store_s0, void, env, int) DEF_HELPER_2(probe_hvx_stores, void, env, int) DEF_HELPER_2(probe_pkt_scalar_hvx_stores, void, env, int) +DEF_HELPER_FLAGS_4(gvec_sabsdiff_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabsdiff_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabsdiff_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabsdiff_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + #if !defined(CONFIG_USER_ONLY) DEF_HELPER_3(raise_stack_overflow, void, env, i32, i32) DEF_HELPER_2(swi, void, env, i32) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 8fb3d43c33..23894ff3d2 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -35,6 +35,7 @@ #include "mmvec/macros.h" #include "op_helper.h" #include "cpu_helper.h" +#include "tcg/tcg-gvec-desc.h" #include "translate.h" #ifndef CONFIG_USER_ONLY #include "hw/hexagon/hexagon_globalreg.h" @@ -1961,3 +1962,23 @@ void HELPER(pending_interrupt)(CPUHexagonState *env) printf("ERROR: bogus helper: " #tag "\n") #include "helper_funcs_generated.c.inc" + +#define DO_ABSDIFF(NAME, TYPE, UTYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + UTYPE *d = vd; \ + TYPE *n = vn, *m = vm; \ + \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = n[i] < m[i] ? (UTYPE)m[i] - (UTYPE)n[i] \ + : (UTYPE)n[i] - (UTYPE)m[i]; \ + } \ +} + +DO_ABSDIFF(gvec_sabsdiff_h, int16_t, uint16_t) +DO_ABSDIFF(gvec_sabsdiff_w, int32_t, uint32_t) +DO_ABSDIFF(gvec_uabsdiff_b, uint8_t, uint8_t) +DO_ABSDIFF(gvec_uabsdiff_h, uint16_t, uint16_t) + +#undef DO_ABSDIFF From ffbb6bbca5d0b74fb24ece36356963267f8b34ab Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 7 Aug 2026 08:22:39 -0700 Subject: [PATCH 418/860] target/hexagon: add GVec overrides for HVX vector average Reviewed-by: Marco Liebel Link: https://lore.kernel.org/qemu-devel/20260807152241.1576334-6-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/gen_tcg_hvx.h | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/target/hexagon/gen_tcg_hvx.h b/target/hexagon/gen_tcg_hvx.h index cbf84a8cae..2a342cdee6 100644 --- a/target/hexagon/gen_tcg_hvx.h +++ b/target/hexagon/gen_tcg_hvx.h @@ -342,6 +342,68 @@ static inline void assert_vhist_tmp(DisasContext *ctx) gen_gvec_sabsdiff(MO_32, VdV_off, VuV_off, VvV_off, \ VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE) +#define fGEN_TCG_VEC_AVG(VECE, SHIFT_FN) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + tcg_gen_gvec_and(MO_64, tmpoff, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_xor(MO_64, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + SHIFT_FN(VECE, VdV_off, VdV_off, 1, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_add(VECE, VdV_off, tmpoff, VdV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + } while (0) + +#define fGEN_TCG_V6_vavgub(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_8, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguh(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_16, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguw(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_32, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavgb(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_8, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavgh(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_16, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavgw(SHORTCODE) \ + fGEN_TCG_VEC_AVG(MO_32, tcg_gen_gvec_sari) + +#define fGEN_TCG_VEC_AVGRND(VECE, SHIFT_FN) \ + do { \ + intptr_t tmpoff = offsetof(CPUHexagonState, vtmp); \ + tcg_gen_gvec_or(MO_64, tmpoff, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_xor(MO_64, VdV_off, VuV_off, VvV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + SHIFT_FN(VECE, VdV_off, VdV_off, 1, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + tcg_gen_gvec_sub(VECE, VdV_off, tmpoff, VdV_off, \ + VECTOR_SIZE_BYTE, VECTOR_SIZE_BYTE); \ + } while (0) + +#define fGEN_TCG_V6_vavgubrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_8, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguhrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_16, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavguwrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_32, tcg_gen_gvec_shri) + +#define fGEN_TCG_V6_vavgbrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_8, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavghrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_16, tcg_gen_gvec_sari) + +#define fGEN_TCG_V6_vavgwrnd(SHORTCODE) \ + fGEN_TCG_VEC_AVGRND(MO_32, tcg_gen_gvec_sari) + /* Vector shift right - various forms */ #define fGEN_TCG_V6_vasrh(SHORTCODE) \ do { \ From ead4d3a8d874f7f59fec909ea17a66b056b39ea5 Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 7 Aug 2026 08:22:40 -0700 Subject: [PATCH 419/860] tests/tcg/hexagon: add HVX test for V6_vsubwsat saturation Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260807152241.1576334-7-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/tcg/hexagon/hvx_misc.c | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index 90c3733da0..641cff59ab 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include int err; @@ -386,6 +388,47 @@ static void test_vsubuwsat_dv(void) check_output_w(__LINE__, 2); } +static void test_vsubwsat(void) +{ + const int32_t x0 = INT32_MIN; + const int32_t y0 = 1; + const int32_t x1 = INT32_MAX; + const int32_t y1 = -1; + HVX_Vector v0; + HVX_Vector v1; + HVX_Vector vres; + + /* INT32_MIN - 1 underflows and must saturate to INT32_MIN */ + memset(expect, 0x12, sizeof(MMVector)); + memset(output, 0x34, sizeof(MMVector)); + + v0 = Q6_V_vsplat_R(x0); + v1 = Q6_V_vsplat_R(y0); + vres = Q6_Vw_vsub_VwVw_sat(v0, v1); + memcpy(&output[0], &vres, sizeof(MMVector)); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[0].w[j] = INT32_MIN; + } + + check_output_w(__LINE__, 1); + + /* INT32_MAX - (-1) overflows and must saturate to INT32_MAX */ + memset(expect, 0x12, sizeof(MMVector)); + memset(output, 0x34, sizeof(MMVector)); + + v0 = Q6_V_vsplat_R(x1); + v1 = Q6_V_vsplat_R(y1); + vres = Q6_Vw_vsub_VwVw_sat(v0, v1); + memcpy(&output[0], &vres, sizeof(MMVector)); + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + expect[0].w[j] = INT32_MAX; + } + + check_output_w(__LINE__, 1); +} + static void test_load_tmp_predicated(void) { void *p0 = buffer0; @@ -530,6 +573,7 @@ int main() test_vadduwsat(); test_vsubuwsat_dv(); + test_vsubwsat(); test_load_tmp_predicated(); test_load_cur_predicated(); From dde6dc062adbae1c1a1928a87cc425c310339c2b Mon Sep 17 00:00:00 2001 From: Brian Cain Date: Fri, 7 Aug 2026 08:22:41 -0700 Subject: [PATCH 420/860] tests/tcg/hexagon: add HVX tests for vabsdiff Cover the four vabsdiff variants. The expected value is computed in int64_t to avoid overflowing the source element type. Reviewed-by: Pierrick Bouvier Link: https://lore.kernel.org/qemu-devel/20260807152241.1576334-8-brian.cain@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/tcg/hexagon/hvx_misc.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/tcg/hexagon/hvx_misc.c b/tests/tcg/hexagon/hvx_misc.c index 641cff59ab..32a3661a86 100644 --- a/tests/tcg/hexagon/hvx_misc.c +++ b/tests/tcg/hexagon/hvx_misc.c @@ -317,6 +317,34 @@ TEST_VEC_OP2(vand, vand, , d, 8, &) TEST_VEC_OP2(vor, vor, , d, 8, |) TEST_VEC_OP1(vnot, vnot, , d, 8, ~) +#define TEST_VEC_ABSDIFF(NAME, INTRINSIC, SRC_FIELD, DST_FIELD, \ + CHECK_FIELD, FIELDSZ) \ +static inline void test_##NAME(void) \ +{ \ + HVX_Vector v0; \ + HVX_Vector v1; \ + HVX_Vector vres; \ + for (int i = 0; i < BUFSIZE; i++) { \ + memcpy(&v0, &buffer0[i], sizeof(MMVector)); \ + memcpy(&v1, &buffer1[i], sizeof(MMVector)); \ + vres = INTRINSIC(v0, v1); \ + memcpy(&output[i], &vres, sizeof(MMVector)); \ + } \ + for (int i = 0; i < BUFSIZE; i++) { \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / FIELDSZ; j++) { \ + int64_t diff = (int64_t)buffer0[i].SRC_FIELD[j] - \ + (int64_t)buffer1[i].SRC_FIELD[j]; \ + expect[i].DST_FIELD[j] = diff < 0 ? -diff : diff; \ + } \ + } \ + check_output_##CHECK_FIELD(__LINE__, BUFSIZE); \ +} + +TEST_VEC_ABSDIFF(vabsdiffub, Q6_Vub_vabsdiff_VubVub, ub, ub, b, 1) +TEST_VEC_ABSDIFF(vabsdiffuh, Q6_Vuh_vabsdiff_VuhVuh, uh, uh, h, 2) +TEST_VEC_ABSDIFF(vabsdiffh, Q6_Vuh_vabsdiff_VhVh, h, uh, h, 2) +TEST_VEC_ABSDIFF(vabsdiffw, Q6_Vuw_vabsdiff_VwVw, w, uw, w, 4) + TEST_PRED_OP2(pred_or, or, |, "") TEST_PRED_OP2(pred_or_n, or, |, "!") TEST_PRED_OP2(pred_and, and, &, "") @@ -575,6 +603,11 @@ int main() test_vsubuwsat_dv(); test_vsubwsat(); + test_vabsdiffub(); + test_vabsdiffuh(); + test_vabsdiffh(); + test_vabsdiffw(); + test_load_tmp_predicated(); test_load_cur_predicated(); From 19148de4986f9e93392849a0e4fa75244e780e9f Mon Sep 17 00:00:00 2001 From: Taylor Simpson Date: Mon, 10 Aug 2026 21:22:06 -0600 Subject: [PATCH 421/860] Hexagon (target/hexagon) Clean up disassembly of control and system regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change disassembly of control regs from C{num}/{name} to {name} Change disassembly of system regs from S{num}/r{num} to {name} Signed-off-by: Taylor Simpson Reviewed-by: Brian Cain Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20260811032206.58501-1-ltaylorsimpson@gmail.com Signed-off-by: Brian Cain --- target/hexagon/gen_printinsn.py | 14 +++-- target/hexagon/printinsn.c | 99 +++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 12 deletions(-) diff --git a/target/hexagon/gen_printinsn.py b/target/hexagon/gen_printinsn.py index d5f969960a..cf1a12adbc 100755 --- a/target/hexagon/gen_printinsn.py +++ b/target/hexagon/gen_printinsn.py @@ -28,13 +28,14 @@ import argparse ## Generate data for printing each instruction (format string + operands) ## def regprinter(m): - str = m.group(1) - str += ":".join(["%d"] * len(m.group(2))) - str += m.group(3) if ("S" in m.group(1)) and (len(m.group(2)) == 1): - str += "/%s" + str = "%s" elif ("C" in m.group(1)) and (len(m.group(2)) == 1): - str += "/%s" + str = "%s" + else: + str = m.group(1) + str += ":".join(["%d"] * len(m.group(2))) + str += m.group(3) return str @@ -142,11 +143,12 @@ def main(): else: regno = ri if len(b) == 1: - f.write(f", insn->regno[{regno}]") if "S" in a: f.write(f", sreg2str(insn->regno[{regno}])") elif "C" in a: f.write(f", creg2str(insn->regno[{regno}])") + else: + f.write(f", insn->regno[{regno}]") elif len(b) == 2: f.write(f", insn->regno[{regno}] + 1" f", insn->regno[{regno}]") else: diff --git a/target/hexagon/printinsn.c b/target/hexagon/printinsn.c index a7e46f4bcd..1673e95421 100644 --- a/target/hexagon/printinsn.c +++ b/target/hexagon/printinsn.c @@ -23,18 +23,105 @@ #include "internal.h" #include "decode.h" -static const char *sreg2str(unsigned int reg) +/* + * Used when there is some sort of error and we can't figure out the real + * system register name + */ +static const char *const generic_sreg_names[256] = { + "S000", "S001", "S002", "S003", "S004", "S005", "S006", "S007", + "S008", "S009", "S010", "S011", "S012", "S013", "S014", "S015", + "S016", "S017", "S018", "S019", "S020", "S021", "S022", "S023", + "S024", "S025", "S026", "S027", "S028", "S029", "S030", "S031", + "S032", "S033", "S034", "S035", "S036", "S037", "S038", "S039", + "S040", "S041", "S042", "S043", "S044", "S045", "S046", "S047", + "S048", "S049", "S050", "S051", "S052", "S053", "S054", "S055", + "S056", "S057", "S058", "S059", "S060", "S061", "S062", "S063", + "S064", "S065", "S066", "S067", "S068", "S069", "S070", "S071", + "S072", "S073", "S074", "S075", "S076", "S077", "S078", "S079", + "S080", "S081", "S082", "S083", "S084", "S085", "S086", "S087", + "S088", "S089", "S090", "S091", "S092", "S093", "S094", "S095", + "S096", "S097", "S098", "S099", "S100", "S101", "S102", "S103", + "S104", "S105", "S106", "S107", "S108", "S109", "S110", "S111", + "S112", "S113", "S114", "S115", "S116", "S117", "S118", "S119", + "S120", "S121", "S122", "S123", "S124", "S125", "S126", "S127", + "S128", "S129", "S130", "S131", "S132", "S133", "S134", "S135", + "S136", "S137", "S138", "S139", "S140", "S141", "S142", "S143", + "S144", "S145", "S146", "S147", "S148", "S149", "S150", "S151", + "S152", "S153", "S154", "S155", "S156", "S157", "S158", "S159", + "S160", "S161", "S162", "S163", "S164", "S165", "S166", "S167", + "S168", "S169", "S170", "S171", "S172", "S173", "S174", "S175", + "S176", "S177", "S178", "S179", "S180", "S181", "S182", "S183", + "S184", "S185", "S186", "S187", "S188", "S189", "S190", "S191", + "S192", "S193", "S194", "S195", "S196", "S197", "S198", "S199", + "S200", "S201", "S202", "S203", "S204", "S205", "S206", "S207", + "S208", "S209", "S210", "S211", "S212", "S213", "S214", "S215", + "S216", "S217", "S218", "S219", "S220", "S221", "S222", "S223", + "S224", "S225", "S226", "S227", "S228", "S229", "S230", "S231", + "S232", "S233", "S234", "S235", "S236", "S237", "S238", "S239", + "S240", "S241", "S242", "S243", "S244", "S245", "S246", "S247", + "S248", "S249", "S250", "S251", "S252", "S253", "S254", "S255", +}; + +static const char *sreg2str(uint8_t reg) { - if (reg < TOTAL_PER_THREAD_REGS) { - return hexagon_regnames[reg]; +#ifndef CONFIG_USER_ONLY + if (reg < NUM_SREGS) { + return hexagon_sregnames[reg]; } else { - return "???"; + return generic_sreg_names[reg]; } +#else + return generic_sreg_names[reg]; +#endif } -static const char *creg2str(unsigned int reg) +/* + * Used when there is some sort of error and we can't figure out the real + * control register name + */ +static const char *const generic_creg_names[256] = { + "C000", "C001", "C002", "C003", "C004", "C005", "C006", "C007", + "C008", "C009", "C010", "C011", "C012", "C013", "C014", "C015", + "C016", "C017", "C018", "C019", "C020", "C021", "C022", "C023", + "C024", "C025", "C026", "C027", "C028", "C029", "C030", "C031", + "C032", "C033", "C034", "C035", "C036", "C037", "C038", "C039", + "C040", "C041", "C042", "C043", "C044", "C045", "C046", "C047", + "C048", "C049", "C050", "C051", "C052", "C053", "C054", "C055", + "C056", "C057", "C058", "C059", "C060", "C061", "C062", "C063", + "C064", "C065", "C066", "C067", "C068", "C069", "C070", "C071", + "C072", "C073", "C074", "C075", "C076", "C077", "C078", "C079", + "C080", "C081", "C082", "C083", "C084", "C085", "C086", "C087", + "C088", "C089", "C090", "C091", "C092", "C093", "C094", "C095", + "C096", "C097", "C098", "C099", "C100", "C101", "C102", "C103", + "C104", "C105", "C106", "C107", "C108", "C109", "C110", "C111", + "C112", "C113", "C114", "C115", "C116", "C117", "C118", "C119", + "C120", "C121", "C122", "C123", "C124", "C125", "C126", "C127", + "C128", "C129", "C130", "C131", "C132", "C133", "C134", "C135", + "C136", "C137", "C138", "C139", "C140", "C141", "C142", "C143", + "C144", "C145", "C146", "C147", "C148", "C149", "C150", "C151", + "C152", "C153", "C154", "C155", "C156", "C157", "C158", "C159", + "C160", "C161", "C162", "C163", "C164", "C165", "C166", "C167", + "C168", "C169", "C170", "C171", "C172", "C173", "C174", "C175", + "C176", "C177", "C178", "C179", "C180", "C181", "C182", "C183", + "C184", "C185", "C186", "C187", "C188", "C189", "C190", "C191", + "C192", "C193", "C194", "C195", "C196", "C197", "C198", "C199", + "C200", "C201", "C202", "C203", "C204", "C205", "C206", "C207", + "C208", "C209", "C210", "C211", "C212", "C213", "C214", "C215", + "C216", "C217", "C218", "C219", "C220", "C221", "C222", "C223", + "C224", "C225", "C226", "C227", "C228", "C229", "C230", "C231", + "C232", "C233", "C234", "C235", "C236", "C237", "C238", "C239", + "C240", "C241", "C242", "C243", "C244", "C245", "C246", "C247", + "C248", "C249", "C250", "C251", "C252", "C253", "C254", "C255", +}; + +static const char *creg2str(uint8_t reg) { - return sreg2str(reg + HEX_REG_SA0); + uint8_t gpr = reg + HEX_REG_SA0; + if (gpr < TOTAL_PER_THREAD_REGS) { + return hexagon_regnames[gpr]; + } else { + return generic_creg_names[reg]; + } } static void snprintinsn(GString *buf, Insn *insn) From 770e282e50e6f6bafe76c3e6b41f9cc8acef7ad9 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:51 -0700 Subject: [PATCH 422/860] target/hexagon: fix incorrect/too-permissive HVX encodings The following encodings have become stricter since v68: - V6_vunpackob, V6_vunpackoh: ---00 -> --000 - V6_vaddbq/hq/wq, V6_vaddbnq/hnq/wnq: ---01 -> --001 - V6_vsubbq/hq, V6_vsubwq/bnq/hnq/wnq: ---01/---10 -> --001/--010 - V6_vhist, V6_vwhist128/256, V6_vwhist128/256_sat: ---00 -> --000 - V6_vhistq, V6_vwhist128/256q, V6_vwhist128/256q_sat: ---10 -> --010 Pre v68 compilers, by default, already use "0" for the non-specified bit that changed in v68, so unless someone is manually writing the binary encoding, this should not cause any backwards incompatibility with pre-v68 binaries. Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/1fe4b8a0fcae6705a591b1b5131e28f6d8062eed.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/imported/mmvec/encode_ext.def | 48 ++++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index 402438f566..6d70086b5f 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -647,36 +647,36 @@ DEF_ENC(V6_vsubububb_sat, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 101 ddddd") DEF_ENC(V6_vmpyewuh_64, ICLASS_CJ" 1 110 101 vvvvv PP 0 uuuuu 110 ddddd") DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ----- PP 1 ----- ----- ---","Vx32=Vu32") -DEF_ENC(V6_vunpackob, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 000 xxxxx") // -DEF_ENC(V6_vunpackoh, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vunpackob, ICLASS_CJ" 1 110 --0 --000 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vunpackoh, ICLASS_CJ" 1 110 --0 --000 PP 1 uuuuu 001 xxxxx") // //DEF_ENC(V6_vunpackow, ICLASS_CJ" 1 110 --0 ---00 PP 1 uuuuu 010 xxxxx") // -DEF_ENC(V6_vhist, ICLASS_CJ" 1 110 --0 ---00 PP 1 -000- 100 -----") -DEF_ENC(V6_vwhist256, ICLASS_CJ" 1 110 --0 ---00 PP 1 -0010 100 -----") -DEF_ENC(V6_vwhist256_sat, ICLASS_CJ" 1 110 --0 ---00 PP 1 -0011 100 -----") -DEF_ENC(V6_vwhist128, ICLASS_CJ" 1 110 --0 ---00 PP 1 -010- 100 -----") -DEF_ENC(V6_vwhist128m, ICLASS_CJ" 1 110 --0 ---00 PP 1 -011i 100 -----") +DEF_ENC(V6_vhist, ICLASS_CJ" 1 110 --0 --000 PP 1 -000- 100 -----") +DEF_ENC(V6_vwhist256, ICLASS_CJ" 1 110 --0 --000 PP 1 -0010 100 -----") +DEF_ENC(V6_vwhist256_sat, ICLASS_CJ" 1 110 --0 --000 PP 1 -0011 100 -----") +DEF_ENC(V6_vwhist128, ICLASS_CJ" 1 110 --0 --000 PP 1 -010- 100 -----") +DEF_ENC(V6_vwhist128m, ICLASS_CJ" 1 110 --0 --000 PP 1 -011i 100 -----") DEF_FIELDROW_DESC32( ICLASS_CJ" 1 110 --0 ----- PP 1 ----- ----- ---","if (Qv4) Vx32=Vu32") -DEF_ENC(V6_vaddbq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 000 xxxxx") // -DEF_ENC(V6_vaddhq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 001 xxxxx") // -DEF_ENC(V6_vaddwq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 010 xxxxx") // -DEF_ENC(V6_vaddbnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 011 xxxxx") // -DEF_ENC(V6_vaddhnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 100 xxxxx") // -DEF_ENC(V6_vaddwnq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 101 xxxxx") // -DEF_ENC(V6_vsubbq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 110 xxxxx") // -DEF_ENC(V6_vsubhq, ICLASS_CJ" 1 110 vv0 ---01 PP 1 uuuuu 111 xxxxx") // +DEF_ENC(V6_vaddbq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vaddhq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vaddwq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vaddbnq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vaddhnq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 100 xxxxx") // +DEF_ENC(V6_vaddwnq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 101 xxxxx") // +DEF_ENC(V6_vsubbq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 110 xxxxx") // +DEF_ENC(V6_vsubhq, ICLASS_CJ" 1 110 vv0 --001 PP 1 uuuuu 111 xxxxx") // -DEF_ENC(V6_vsubwq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 000 xxxxx") // -DEF_ENC(V6_vsubbnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 001 xxxxx") // -DEF_ENC(V6_vsubhnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 010 xxxxx") // -DEF_ENC(V6_vsubwnq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 uuuuu 011 xxxxx") // +DEF_ENC(V6_vsubwq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 000 xxxxx") // +DEF_ENC(V6_vsubbnq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 001 xxxxx") // +DEF_ENC(V6_vsubhnq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 010 xxxxx") // +DEF_ENC(V6_vsubwnq, ICLASS_CJ" 1 110 vv0 --010 PP 1 uuuuu 011 xxxxx") // -DEF_ENC(V6_vhistq, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --00- 100 -----") -DEF_ENC(V6_vwhist256q, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --010 100 -----") -DEF_ENC(V6_vwhist256q_sat, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --011 100 -----") -DEF_ENC(V6_vwhist128q, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --10- 100 -----") -DEF_ENC(V6_vwhist128qm, ICLASS_CJ" 1 110 vv0 ---10 PP 1 --11i 100 -----") +DEF_ENC(V6_vhistq, ICLASS_CJ" 1 110 vv0 --010 PP 1 --00- 100 -----") +DEF_ENC(V6_vwhist256q, ICLASS_CJ" 1 110 vv0 --010 PP 1 --010 100 -----") +DEF_ENC(V6_vwhist256q_sat, ICLASS_CJ" 1 110 vv0 --010 PP 1 --011 100 -----") +DEF_ENC(V6_vwhist128q, ICLASS_CJ" 1 110 vv0 --010 PP 1 --10- 100 -----") +DEF_ENC(V6_vwhist128qm, ICLASS_CJ" 1 110 vv0 --010 PP 1 --11i 100 -----") DEF_ENC(V6_vandvqv, ICLASS_CJ" 1 110 vv0 ---11 PP 1 uuuuu 000 ddddd") From e1245d0ea4552895429a10e6eb51f148cea4d4c0 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:52 -0700 Subject: [PATCH 423/860] target/hexagon/cpu: add HVX IEEE FP extension This flag will be used to control the HVX IEEE float instructions, which are only available at some Hexagon cores. When unavailable, the instruction effectively only set the destination registers to 0. Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/10fb5b86db60a465e51db2cf73185307a1ec0895.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/attribs_def.h.inc | 3 +++ target/hexagon/cpu.c | 1 + target/hexagon/cpu.h | 1 + target/hexagon/gen_tcg_funcs.py | 12 ++++++++++++ target/hexagon/hex_common.py | 25 +++++++++++++++++++++++++ target/hexagon/translate.c | 1 + target/hexagon/translate.h | 1 + 7 files changed, 44 insertions(+) diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index 6c55063a30..b1c155ea6d 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -196,5 +196,8 @@ DEF_ATTRIB(NOTE_SHIFT_RESOURCE, "Uses the HVX shift resource.", "", "") DEF_ATTRIB(RESTRICT_NOSLOT1_STORE, "Packet must not have slot 1 store", "", "") DEF_ATTRIB(RESTRICT_LATEPRED, "Predicate can not be used as a .new.", "", "") +/* HVX IEEE FP extension attributes */ +DEF_ATTRIB(HVX_IEEE_FP, "HVX IEEE FP extension instruction", "", "") + /* Keep this as the last attribute: */ DEF_ATTRIB(ZZ_LASTATTRIB, "Last attribute in the file", "", "") diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 75cae45029..f25878cd67 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -75,6 +75,7 @@ static const Property hexagon_cpu_properties[] = { DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, qdev_prop_uint32, target_ulong), DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true), + DEFINE_PROP_BOOL("ieee-fp", HexagonCPU, ieee_fp_extension, true), }; const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index f6c3c63932..502f27983b 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -194,6 +194,7 @@ struct ArchCPU { bool lldb_compat; target_ulong lldb_stack_adjust; bool short_circuit; + bool ieee_fp_extension; #ifndef CONFIG_USER_ONLY HexagonTLBState *tlb; uint32_t boot_addr; diff --git a/target/hexagon/gen_tcg_funcs.py b/target/hexagon/gen_tcg_funcs.py index 43e6807749..2592acf21e 100755 --- a/target/hexagon/gen_tcg_funcs.py +++ b/target/hexagon/gen_tcg_funcs.py @@ -23,6 +23,15 @@ import string import hex_common from textwrap import dedent +def gen_disabled_ieee_insn(f, tag, regs): + f.write(" if (!ctx->ieee_fp_extension) {\n") + for regtype, regid in regs: + reg = hex_common.get_register(tag, regtype, regid) + if reg.is_hvx_reg() and reg.is_written(): + reg.gen_zero(f) + f.write(" return;\n") + f.write(" }\n") + ## ## Generate the TCG code to call the helper ## For A2_add: Rd32=add(Rs32,Rt32), { RdV=RsV+RtV;} @@ -74,6 +83,9 @@ def gen_tcg_func(f, tag, regs, imms): i = 1 if immlett.isupper() else 0 f.write(f" int {hex_common.imm_name(immlett)} = insn->immed[{i}];\n") + if "A_HVX_IEEE_FP" in hex_common.attribdict[tag]: + gen_disabled_ieee_insn(f, tag, regs) + if hex_common.is_idef_parser_enabled(tag): gpr_operands = [ hex_common.get_register(tag, regtype, regid) diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 09a025ba13..ce5e38bea4 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -784,6 +784,11 @@ class VRegDest(Register, Hvx, Dest): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVector), sizeof(MMVector), 0); + """)) def gen_write(self, f, tag): pass def helper_hvx_desc(self, f): @@ -850,6 +855,11 @@ class VRegReadWrite(Register, Hvx, ReadWrite): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVector), sizeof(MMVector), 0); + """)) def gen_write(self, f, tag): pass def helper_hvx_desc(self, f): @@ -882,6 +892,11 @@ class VRegTmp(Register, Hvx, ReadWrite): vreg_src_off(ctx, {self.reg_num}), sizeof(MMVector), sizeof(MMVector)); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVector), sizeof(MMVector), 0); + """)) def gen_write(self, f, tag): f.write(code_fmt(f"""\ gen_vreg_write(ctx, {self.hvx_off()}, {self.reg_num}, @@ -915,6 +930,11 @@ class VRegPairDest(Register, Hvx, Dest): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVectorPair), sizeof(MMVectorPair), 0); + """)) def gen_write(self, f, tag): pass def helper_hvx_desc(self, f): @@ -974,6 +994,11 @@ class VRegPairReadWrite(Register, Hvx, ReadWrite): TCGv_ptr {self.reg_tcg()} = tcg_temp_new_ptr(); tcg_gen_addi_ptr({self.reg_tcg()}, tcg_env, {self.hvx_off()}); """)) + def gen_zero(self, f): + f.write(code_fmt(f"""\ + tcg_gen_gvec_dup_imm(MO_64, {self.hvx_off()}, + sizeof(MMVectorPair), sizeof(MMVectorPair), 0); + """)) def gen_write(self, f, tag): f.write(code_fmt(f"""\ gen_vreg_write_pair(ctx, {self.hvx_off()}, {self.reg_num}, diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 199b4f8c2e..58c84df66e 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1211,6 +1211,7 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP); ctx->short_circuit = hex_cpu->short_circuit; ctx->hex_def = HEXAGON_CPU_GET_CLASS(hex_cpu)->hex_def; + ctx->ieee_fp_extension = hex_cpu->ieee_fp_extension; #ifndef CONFIG_USER_ONLY ctx->num_cycles = 0; ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h index 2fca157553..3c5773e2c7 100644 --- a/target/hexagon/translate.h +++ b/target/hexagon/translate.h @@ -81,6 +81,7 @@ typedef struct DisasContext { target_ulong branch_dest; bool is_tight_loop; bool short_circuit; + bool ieee_fp_extension; bool read_after_write; bool has_hvx_overlap; TCGv new_value[TOTAL_PER_THREAD_REGS]; From 5995de98c4699b3246f1dffa86b90f677b6e7684 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:53 -0700 Subject: [PATCH 424/860] hexagon: group cpu configurations in their own struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be used in a follow up commit. Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/8f9a2e2ccfd2eeda73a63d1a6abbfd6e5458b44c.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/cpu.c | 14 +++++++------- target/hexagon/cpu.h | 10 +++------- target/hexagon/cpu_bits.h | 7 +++++++ target/hexagon/translate.c | 6 +++--- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index f25878cd67..3bc2a2efa9 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -71,11 +71,11 @@ static const Property hexagon_cpu_properties[] = { TYPE_HEXAGON_GLOBALREG, HexagonGlobalRegState *), DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0), #endif - DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false), - DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0, - qdev_prop_uint32, target_ulong), - DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true), - DEFINE_PROP_BOOL("ieee-fp", HexagonCPU, ieee_fp_extension, true), + DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, cfg.lldb_compat, false), + DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, cfg.lldb_stack_adjust, + 0, qdev_prop_uint32, target_ulong), + DEFINE_PROP_BOOL("short-circuit", HexagonCPU, cfg.short_circuit, true), + DEFINE_PROP_BOOL("ieee-fp", HexagonCPU, cfg.ieee_fp_extension, true), }; const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = { @@ -127,7 +127,7 @@ const char * const hexagon_gregnames[] = { static target_ulong adjust_stack_ptrs(CPUHexagonState *env, target_ulong addr) { HexagonCPU *cpu = env_archcpu(env); - target_ulong stack_adjust = cpu->lldb_stack_adjust; + target_ulong stack_adjust = cpu->cfg.lldb_stack_adjust; target_ulong stack_start = env->stack_start; target_ulong stack_size = 0x10000; @@ -239,7 +239,7 @@ static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags) { HexagonCPU *cpu = env_archcpu(env); - if (cpu->lldb_compat) { + if (cpu->cfg.lldb_compat) { /* * When comparing with LLDB, it doesn't step through single-cycle * hardware loops the same way. So, we just skip them here diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index 502f27983b..b0d726bf02 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -186,15 +186,13 @@ typedef struct HexagonCPUClass { const HexagonCPUDef *hex_def; } HexagonCPUClass; +#include "cpu_bits.h" + struct ArchCPU { CPUState parent_obj; CPUHexagonState env; - - bool lldb_compat; - target_ulong lldb_stack_adjust; - bool short_circuit; - bool ieee_fp_extension; + HexagonCPUConfig cfg; #ifndef CONFIG_USER_ONLY HexagonTLBState *tlb; uint32_t boot_addr; @@ -204,8 +202,6 @@ struct ArchCPU { #endif }; -#include "cpu_bits.h" - FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1) FIELD(TB_FLAGS, MMU_INDEX, 1, 3) FIELD(TB_FLAGS, PCYCLE_ENABLED, 4, 1) diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 164e74c782..797ecabca9 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -21,6 +21,13 @@ #include "qemu/bitops.h" #include "cpu-qom.h" +typedef struct HexagonCPUConfig { + bool lldb_compat; + uint32_t lldb_stack_adjust; + bool short_circuit; + bool ieee_fp_extension; +} HexagonCPUConfig; + #define PCALIGN 4 #define PCALIGN_MASK (PCALIGN - 1) diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 58c84df66e..06a8159d28 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -1209,9 +1209,9 @@ static void hexagon_tr_init_disas_context(DisasContextBase *dcbase, ctx->num_hvx_insns = 0; ctx->branch_cond = TCG_COND_NEVER; ctx->is_tight_loop = FIELD_EX32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP); - ctx->short_circuit = hex_cpu->short_circuit; + ctx->short_circuit = hex_cpu->cfg.short_circuit; ctx->hex_def = HEXAGON_CPU_GET_CLASS(hex_cpu)->hex_def; - ctx->ieee_fp_extension = hex_cpu->ieee_fp_extension; + ctx->ieee_fp_extension = hex_cpu->cfg.ieee_fp_extension; #ifndef CONFIG_USER_ONLY ctx->num_cycles = 0; ctx->pcycle_enabled = FIELD_EX32(hex_flags, TB_FLAGS, PCYCLE_ENABLED); @@ -1268,7 +1268,7 @@ static void hexagon_tr_translate_packet(DisasContextBase *dcbase, CPUState *cpu) * so end the TLB after every packet. */ HexagonCPU *hex_cpu = env_archcpu(env); - if (hex_cpu->lldb_compat && qemu_loglevel_mask(CPU_LOG_TB_CPU)) { + if (hex_cpu->cfg.lldb_compat && qemu_loglevel_mask(CPU_LOG_TB_CPU)) { ctx->base.is_jmp = DISAS_TOO_MANY; } } From 97a75a12d6875b0b86211ea5699d1f7a4825e131 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:54 -0700 Subject: [PATCH 425/860] hexagon: print info on "-d in_asm" for disabled IEEE FP instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When cpu->cfg.ieee_fp_extension is off, IEEE FP instructions don't get executed. Let's print that info on the "-d in_asm" output to help users. This will generate an output like the following: 0x00020e30: 0x1f82e1c0 { V0.sf = vadd(V1.sf,V2.sf) (disabled: no ieee_fp) } Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/1bdc772e4a795ecd9f5bf2b7e7143cc4b297318c.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- disas/hexagon.c | 4 ++-- target/hexagon/cpu.c | 5 ++++- target/hexagon/cpu_bits.h | 5 +++-- target/hexagon/decode.c | 4 ++-- target/hexagon/printinsn.c | 9 +++++++-- target/hexagon/printinsn.h | 2 +- 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/disas/hexagon.c b/disas/hexagon.c index 36b8321c26..e2d3804606 100644 --- a/disas/hexagon.c +++ b/disas/hexagon.c @@ -31,7 +31,6 @@ int print_insn_hexagon(bfd_vma memaddr, struct disassemble_info *info) { - const HexagonCPUDef *hex_def = (const HexagonCPUDef *)info->target_info; uint32_t words[PACKET_WORDS_MAX]; bool found_end = false; GString *buf; @@ -58,8 +57,9 @@ int print_insn_hexagon(bfd_vma memaddr, struct disassemble_info *info) return PACKET_WORDS_MAX * sizeof(uint32_t); } + const HexagonCPUConfig *cfg = info->target_info; buf = g_string_sized_new(PACKET_BUFFER_LEN); - len = disassemble_hexagon(words, i, memaddr, buf, hex_def); + len = disassemble_hexagon(words, i, memaddr, buf, cfg); (*info->fprintf_func)(info->stream, "%s", buf->str); g_string_free(buf, true); diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 3bc2a2efa9..666b0cad53 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -444,12 +444,13 @@ static void hexagon_cpu_disas_set_info(const CPUState *cs, const HexagonCPU *cpu = HEXAGON_CPU(cs); info->print_insn = print_insn_hexagon; info->endian = BFD_ENDIAN_LITTLE; - info->target_info = HEXAGON_CPU_GET_CLASS(cpu)->hex_def; + info->target_info = &cpu->cfg; } static void hexagon_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); + HexagonCPU *cpu = HEXAGON_CPU(dev); HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(dev); Error *local_err = NULL; @@ -459,6 +460,8 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) return; } + cpu->cfg.hex_def = mcc->hex_def; + gdb_register_coprocessor(cs, hexagon_hvx_gdb_read_register, hexagon_hvx_gdb_write_register, gdb_find_static_feature("hexagon-hvx.xml")); diff --git a/target/hexagon/cpu_bits.h b/target/hexagon/cpu_bits.h index 797ecabca9..a8fba4aa18 100644 --- a/target/hexagon/cpu_bits.h +++ b/target/hexagon/cpu_bits.h @@ -26,6 +26,7 @@ typedef struct HexagonCPUConfig { uint32_t lldb_stack_adjust; bool short_circuit; bool ieee_fp_extension; + const HexagonCPUDef *hex_def; } HexagonCPUConfig; #define PCALIGN 4 @@ -130,7 +131,7 @@ static inline bool is_packet_end(uint32_t endocing) return ((bits == 0x3) || (bits == 0x0)); } -int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, GString *buf, - const HexagonCPUDef *hex_def); +int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, + GString *buf, const HexagonCPUConfig *cfg); #endif diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c index e6bb7733be..b12e91fe6a 100644 --- a/target/hexagon/decode.c +++ b/target/hexagon/decode.c @@ -856,7 +856,7 @@ int decode_packet(DisasContext *ctx, int max_words, const uint32_t *words, /* Used for "-d in_asm" logging */ int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, - GString *buf, const HexagonCPUDef *hex_def) + GString *buf, const HexagonCPUConfig *cfg) { HexagonCPUDef any_def = { .hex_version = HEX_VER_ANY, /* Allow decode to accept anything */ @@ -867,7 +867,7 @@ int disassemble_hexagon(uint32_t *words, int nwords, bfd_vma pc, ctx.hex_def = &any_def; if (decode_packet(&ctx, nwords, words, &ctx.pkt, true) > 0) { - snprint_a_pkt_disas(buf, &ctx.pkt, words, pc, hex_def); + snprint_a_pkt_disas(buf, &ctx.pkt, words, pc, cfg); return ctx.pkt.encod_pkt_size_in_bytes; } else { for (int i = 0; i < nwords; i++) { diff --git a/target/hexagon/printinsn.c b/target/hexagon/printinsn.c index 1673e95421..023ea12dba 100644 --- a/target/hexagon/printinsn.c +++ b/target/hexagon/printinsn.c @@ -139,7 +139,7 @@ static void snprintinsn(GString *buf, Insn *insn) } void snprint_a_pkt_disas(GString *buf, Packet *pkt, uint32_t *words, - target_ulong pc, const HexagonCPUDef *hex_def) + target_ulong pc, const HexagonCPUConfig *cfg) { bool has_endloop0 = false; bool has_endloop1 = false; @@ -171,12 +171,17 @@ void snprint_a_pkt_disas(GString *buf, Packet *pkt, uint32_t *words, } g_string_append(buf, "\t"); - if (opcode_supported(pkt->insn[i].opcode, hex_def)) { + if (opcode_supported(pkt->insn[i].opcode, cfg->hex_def)) { snprintinsn(buf, &(pkt->insn[i])); } else { g_string_append(buf, ""); } + if (!cfg->ieee_fp_extension && + GET_ATTRIB(pkt->insn[i].opcode, A_HVX_IEEE_FP)) { + g_string_append(buf, " (disabled: no ieee_fp)"); + } + if (i < pkt->num_insns - 1) { /* * Subinstructions are two instructions encoded diff --git a/target/hexagon/printinsn.h b/target/hexagon/printinsn.h index de962b5f2e..c8389408d3 100644 --- a/target/hexagon/printinsn.h +++ b/target/hexagon/printinsn.h @@ -22,6 +22,6 @@ #include "insn.h" void snprint_a_pkt_disas(GString *buf, Packet *pkt, uint32_t *words, - target_ulong pc, const HexagonCPUDef *hex_def); + target_ulong pc, const HexagonCPUConfig *cfg); #endif From 69e46b28228441c5d064fb3b84691cbb23e71296 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:55 -0700 Subject: [PATCH 426/860] target/hexagon: add v68 HVX IEEE float arithmetic insns Add HVX IEEE floating-point arithmetic instructions: - vmpy_sf_sf, vmpy_sf_hf, vmpy_hf_hf: multiply operations - vdmpy_sf_hf: dot-product multiply - vmpy_sf_hf_acc, vmpy_hf_hf_acc, vdmpy_sf_hf_acc: multiply-accumulate - vadd_sf_sf, vsub_sf_sf, vadd_sf_hf, vsub_sf_hf: add/sub with sf output - vadd_hf_hf, vsub_hf_hf: add/sub with hf output Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/8923b6c6bc4fe750c07a07bcd490761f8bab52fe.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/arch.c | 8 ++ target/hexagon/attribs_def.h.inc | 4 + target/hexagon/cpu.c | 3 + target/hexagon/cpu.h | 1 + target/hexagon/hex_common.py | 1 + target/hexagon/imported/mmvec/encode_ext.def | 18 ++++ target/hexagon/imported/mmvec/ext.idef | 101 +++++++++++++++++++ target/hexagon/meson.build | 1 + target/hexagon/mmvec/hvx_ieee_fp.c | 21 ++++ target/hexagon/mmvec/hvx_ieee_fp.h | 18 ++++ target/hexagon/mmvec/macros.h | 1 + target/hexagon/mmvec/mmvec.h | 2 + 12 files changed, 179 insertions(+) create mode 100644 target/hexagon/mmvec/hvx_ieee_fp.c create mode 100644 target/hexagon/mmvec/hvx_ieee_fp.h diff --git a/target/hexagon/arch.c b/target/hexagon/arch.c index 0a400bf7a7..3c417358e0 100644 --- a/target/hexagon/arch.c +++ b/target/hexagon/arch.c @@ -199,6 +199,10 @@ void arch_fpop_start(CPUHexagonState *env) set_float_rounding_mode( softfloat_roundingmodes[fREAD_REG_FIELD(USR, USR_FPRND)], &env->fp_status); + /* + * No need to check env->hvx_fp_status, these instructions don't + * raise exceptions nor interact with usr fields. + */ } #ifdef CONFIG_USER_ONLY @@ -237,6 +241,10 @@ void arch_fpop_end(CPUHexagonState *env, bool pkt_need_commit) SOFTFLOAT_TEST_FLAG(float_flag_overflow, FPOVFF, FPOVFE); SOFTFLOAT_TEST_FLAG(float_flag_underflow, FPUNFF, FPUNFE); } + /* + * No need to check env->hvx_fp_status, these instructions don't + * raise exceptions nor interact with usr fields. + */ } int arch_sf_recip_common(float32 *Rs, float32 *Rt, float32 *Rd, int *adjust, diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index b1c155ea6d..ecd08d02df 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -198,6 +198,10 @@ DEF_ATTRIB(RESTRICT_LATEPRED, "Predicate can not be used as a .new.", "", "") /* HVX IEEE FP extension attributes */ DEF_ATTRIB(HVX_IEEE_FP, "HVX IEEE FP extension instruction", "", "") +DEF_ATTRIB(HVX_IEEE_FP_ACC, "HVX IEEE FP accumulate instruction", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_16, "HVX IEEE FP 16-bit output", "", "") +DEF_ATTRIB(HVX_IEEE_FP_OUT_32, "HVX IEEE FP 32-bit output", "", "") +DEF_ATTRIB(CVI_VX_NO_TMP_LD, "HVX multiply without tmp load", "", "") /* Keep this as the last attribute: */ DEF_ATTRIB(ZZ_LASTATTRIB, "Last attribute in the file", "", "") diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 666b0cad53..0ddd898e6e 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -420,6 +420,9 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType type) set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status); /* Default NaN value: sign bit set, all frac bits set */ set_float_default_nan_pattern(0b11111111, &env->fp_status); + + set_default_nan_mode(1, &env->hvx_fp_status); + set_float_default_nan_pattern(0b01111111, &env->hvx_fp_status); #ifndef CONFIG_USER_ONLY memset(env->t_sreg, 0, sizeof(uint32_t) * NUM_SREGS); memset(env->greg, 0, sizeof(uint32_t) * NUM_GREGS); diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index b0d726bf02..c50fbb3f72 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -152,6 +152,7 @@ typedef struct CPUArchState { MemLog mem_log_stores[STORES_MAX]; float_status fp_status; + float_status hvx_fp_status; target_ulong llsc_addr; target_ulong llsc_val; diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index ce5e38bea4..602f49e1f5 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -253,6 +253,7 @@ def need_env(tag): "A_LOAD" in attribdict[tag] or "A_CVI_GATHER" in attribdict[tag] or "A_CVI_SCATTER" in attribdict[tag] or + "A_HVX_IEEE_FP" in attribdict[tag] or "A_IMPLICIT_WRITES_USR" in attribdict[tag] or "A_PRIV" in attribdict[tag] or "J2_trap" in tag) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index 6d70086b5f..4ce87d09fd 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -804,5 +804,23 @@ DEF_ENC(V6_vmpyewuh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 101 ddddd") DEF_ENC(V6_vmpyowh, ICLASS_CJ" 1 111 111 vvvvv PP 0 uuuuu 111 ddddd") DEF_ENC(V6_vmpyuhvs,"00011111110vvvvvPP1uuuuu111ddddd") +/* IEEE FP multiply instructions */ +DEF_ENC(V6_vmpy_sf_sf,"00011111100vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vmpy_sf_hf,"00011111100vvvvvPP1uuuuu010ddddd") +DEF_ENC(V6_vmpy_hf_hf,"00011111100vvvvvPP1uuuuu011ddddd") +DEF_ENC(V6_vdmpy_sf_hf,"00011111101vvvvvPP1uuuuu110ddddd") + +/* IEEE FP multiply-accumulate instructions */ +DEF_ENC(V6_vmpy_sf_hf_acc,"00011100010vvvvvPP1uuuuu001xxxxx") +DEF_ENC(V6_vmpy_hf_hf_acc,"00011100010vvvvvPP1uuuuu010xxxxx") +DEF_ENC(V6_vdmpy_sf_hf_acc,"00011100010vvvvvPP1uuuuu011xxxxx") + +/* IEEE FP add/sub instructions */ +DEF_ENC(V6_vadd_sf_sf,"00011111100vvvvvPP1uuuuu110ddddd") +DEF_ENC(V6_vsub_sf_sf,"00011111100vvvvvPP1uuuuu111ddddd") +DEF_ENC(V6_vadd_sf_hf,"00011111100vvvvvPP1uuuuu100ddddd") +DEF_ENC(V6_vsub_sf_hf,"00011111100vvvvvPP1uuuuu101ddddd") +DEF_ENC(V6_vadd_hf_hf,"00011111101vvvvvPP1uuuuu111ddddd") +DEF_ENC(V6_vsub_hf_hf,"00011111011vvvvvPP1uuuuu000ddddd") #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 03d31f6181..14df8e4790 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -2895,9 +2895,110 @@ EXTINSN(V6_vprefixqw,"Vd32.w=prefixsum(Qv4)", ATTRIBS(A_EXTENSION,A_CVI,A_CVI_ } } ) +/* KVX - IEEE FP Instructions */ +/* Single pipe, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_32), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +/* Single pipe, 16-bit output */ +#define ITERATOR_INSN_IEEE_FP_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +/* Two pipes: P2 & P3, single output: P2, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_SINGLE_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_32), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* Two pipes: P2 & P3, two outputs, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_32), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* + * single pipe, accumulate instruction, produces 16-bit output, requires 16-bit + * accumulate input + */ +#define ITERATOR_INSN_IEEE_FP_ACC_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_ACC,A_HVX_IEEE_FP_OUT_16,A_CVI_VX_NO_TMP_LD), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* + * single pipe, accumulate instruction, produces 32-bit output, requires 32-bit + * accumulate input + */ +#define ITERATOR_INSN_IEEE_FP_ACC_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_ACC,A_HVX_IEEE_FP_OUT_32,A_CVI_VX_NO_TMP_LD), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* IEEE FP multiply instructions */ +ITERATOR_INSN_IEEE_FP_DOUBLE_SINGLE_32(32, vmpy_sf_sf, + "Vd32.sf=vmpy(Vu32.sf,Vv32.sf)", "Vector IEEE mul: sf", + VdV.sf[i] = float32_mul(VuV.sf[i], VvV.sf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_hf, + "Vdd32.sf=vmpy(Vu32.hf,Vv32.hf)", "Vector IEEE mul: hf widen to sf", + VddV.v[0].sf[i] = fp_mult_sf_hf(VuV.hf[2*i], VvV.hf[2*i], &env->hvx_fp_status); + VddV.v[1].sf[i] = fp_mult_sf_hf(VuV.hf[2*i+1], VvV.hf[2*i+1], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16(16, vmpy_hf_hf, "Vd32.hf=vmpy(Vu32.hf,Vv32.hf)", + "Vector IEEE mul: hf", + VdV.hf[i] = float16_mul(VuV.hf[i], VvV.hf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_32(32, vdmpy_sf_hf, "Vd32.sf=vdmpy(Vu32.hf,Vv32.hf)", + "Vector IEEE mul reduction: hf widen to sf", + VdV.sf[i] = fp_vdmpy(VuV.hf[2*i+1], VuV.hf[2*i], VvV.hf[2*i+1], + VvV.hf[2*i], &env->hvx_fp_status)) + +/* IEEE FP multiply-accumulate instructions */ +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_hf_acc, + "Vxx32.sf+=vmpy(Vu32.hf,Vv32.hf)", "Vector IEEE fma: hf widen to sf", + VxxV.v[0].sf[i] = float32_muladd(f16_to_f32(VuV.hf[2*i]), + f16_to_f32(VvV.hf[2*i]), + VxxV.v[0].sf[i], 0, &env->hvx_fp_status); + VxxV.v[1].sf[i] = float32_muladd(f16_to_f32(VuV.hf[2*i+1]), + f16_to_f32(VvV.hf[2*i+1]), + VxxV.v[1].sf[i], 0, &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_ACC_16(16, vmpy_hf_hf_acc, + "Vx32.hf+=vmpy(Vu32.hf,Vv32.hf)", "Vector IEEE fma: hf", + VxV.hf[i] = float16_muladd(VuV.hf[i], VvV.hf[i], VxV.hf[i], 0, &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_ACC_32(32, vdmpy_sf_hf_acc, + "Vx32.sf+=vdmpy(Vu32.hf,Vv32.hf)", "Vector IEEE fma reduce: hf widen to sf", + VxV.sf[i] = float32_add(fp_vdmpy(VuV.hf[2*i+1], VuV.hf[2*i], + VvV.hf[2*i+1], VvV.hf[2*i], + &env->hvx_fp_status), + VxV.sf[i], &env->hvx_fp_status)) + +/* IEEE FP add/sub instructions */ +ITERATOR_INSN_IEEE_FP_32(32, vadd_sf_sf, "Vd32.sf=vadd(Vu32.sf,Vv32.sf)", + "Vector IEEE add: sf", + VdV.sf[i] = float32_add(VuV.sf[i], VvV.sf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_32(32, vsub_sf_sf, "Vd32.sf=vsub(Vu32.sf,Vv32.sf)", + "Vector IEEE sub: sf", + VdV.sf[i] = float32_sub(VuV.sf[i], VvV.sf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16(16, vadd_hf_hf, "Vd32.hf=vadd(Vu32.hf,Vv32.hf)", + "Vector IEEE add: hf", + VdV.hf[i] = float16_add(VuV.hf[i], VvV.hf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16(16, vsub_hf_hf, "Vd32.hf=vsub(Vu32.hf,Vv32.hf)", + "Vector IEEE sub: hf", + VdV.hf[i] = float16_sub(VuV.hf[i], VvV.hf[i], &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vadd_sf_hf, + "Vdd32.sf=vadd(Vu32.hf,Vv32.hf)", "Vector IEEE add: hf widen to sf", + VddV.v[0].sf[i] = float32_add(f16_to_f32(VuV.hf[2*i]), + f16_to_f32(VvV.hf[2*i]), &env->hvx_fp_status); + VddV.v[1].sf[i] = float32_add(f16_to_f32(VuV.hf[2*i+1]), + f16_to_f32(VvV.hf[2*i+1]), &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vsub_sf_hf, + "Vdd32.sf=vsub(Vu32.hf,Vv32.hf)", "Vector IEEE sub: hf widen to sf", + VddV.v[0].sf[i] = float32_sub(f16_to_f32(VuV.hf[2*i]), + f16_to_f32(VvV.hf[2*i]), &env->hvx_fp_status); + VddV.v[1].sf[i] = float32_sub(f16_to_f32(VuV.hf[2*i+1]), + f16_to_f32(VvV.hf[2*i+1]), &env->hvx_fp_status)) /****************************************************************************** DEBUG Vector/Register Printing diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build index 59cb09c107..4c921eee73 100644 --- a/target/hexagon/meson.build +++ b/target/hexagon/meson.build @@ -252,6 +252,7 @@ hexagon_ss.add(files( 'fma_emu.c', 'mmvec/decode_ext_mmvec.c', 'mmvec/system_ext_mmvec.c', + 'mmvec/hvx_ieee_fp.c', )) hexagon_softmmu_ss.add(files( diff --git a/target/hexagon/mmvec/hvx_ieee_fp.c b/target/hexagon/mmvec/hvx_ieee_fp.c new file mode 100644 index 0000000000..3367226998 --- /dev/null +++ b/target/hexagon/mmvec/hvx_ieee_fp.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hvx_ieee_fp.h" + +float32 fp_mult_sf_hf(float16 a1, float16 a2, float_status *fp_status) +{ + return float32_mul(float16_to_float32(a1, true, fp_status), + float16_to_float32(a2, true, fp_status), fp_status); +} + +float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, + float_status *fp_status) +{ + return float32_add(fp_mult_sf_hf(a1, a3, fp_status), + fp_mult_sf_hf(a2, a4, fp_status), fp_status); +} diff --git a/target/hexagon/mmvec/hvx_ieee_fp.h b/target/hexagon/mmvec/hvx_ieee_fp.h new file mode 100644 index 0000000000..75008deb3b --- /dev/null +++ b/target/hexagon/mmvec/hvx_ieee_fp.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HEXAGON_HVX_IEEE_H +#define HEXAGON_HVX_IEEE_H + +#include "fpu/softfloat.h" + +#define f16_to_f32(A) float16_to_float32((A), true, &env->hvx_fp_status) + +float32 fp_mult_sf_hf(float16 a1, float16 a2, float_status *fp_status); +float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, + float_status *fp_status); + +#endif diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index c7840fbf2e..ac709d8993 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -23,6 +23,7 @@ #include "mmvec/system_ext_mmvec.h" #include "accel/tcg/getpc.h" #include "accel/tcg/probe.h" +#include "mmvec/hvx_ieee_fp.h" #ifndef QEMU_GENERATE #define VdV (*(MMVector *restrict)(VdV_void)) diff --git a/target/hexagon/mmvec/mmvec.h b/target/hexagon/mmvec/mmvec.h index 4a4f6cc980..9841687f83 100644 --- a/target/hexagon/mmvec/mmvec.h +++ b/target/hexagon/mmvec/mmvec.h @@ -41,6 +41,8 @@ typedef union { int16_t h[MAX_VEC_SIZE_BYTES / 2]; uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; int8_t b[MAX_VEC_SIZE_BYTES / 1]; + float32 sf[MAX_VEC_SIZE_BYTES / 4]; + float16 hf[MAX_VEC_SIZE_BYTES / 2]; } MMVector; typedef union { From 2843fc5feb5336a313b906d898037ef6dab94578 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:56 -0700 Subject: [PATCH 427/860] target/hexagon: add v68 HVX IEEE float min/max insns Add HVX IEEE floating-point min/max instructions: - vfmin_hf, vfmin_sf: IEEE floating-point minimum - vfmax_hf, vfmax_sf: IEEE floating-point maximum - vmax_hf, vmax_sf: qfloat IEEE maximum - vmin_hf, vmin_sf: qfloat IEEE minimum The Hexagon qfloat variants are similar to the IEEE-754 ones, but they handle NaN slightly differently. See comment on hvx_ieee_fp.h Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/8e274a7a10a5aae23eb1250db0b4f4250c81f3ef.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/attribs_def.h.inc | 2 + target/hexagon/hex_common.py | 1 + target/hexagon/imported/mmvec/encode_ext.def | 10 ++++ target/hexagon/imported/mmvec/ext.idef | 36 +++++++++++++- target/hexagon/mmvec/hvx_ieee_fp.c | 50 ++++++++++++++++++++ target/hexagon/mmvec/hvx_ieee_fp.h | 6 +++ 6 files changed, 104 insertions(+), 1 deletion(-) diff --git a/target/hexagon/attribs_def.h.inc b/target/hexagon/attribs_def.h.inc index ecd08d02df..d12f6ac4b1 100644 --- a/target/hexagon/attribs_def.h.inc +++ b/target/hexagon/attribs_def.h.inc @@ -84,6 +84,7 @@ DEF_ATTRIB(CVI_SCATTER, "CVI Scatter operation", "", "") DEF_ATTRIB(CVI_SCATTER_RELEASE, "CVI Store Release for scatter", "", "") DEF_ATTRIB(CVI_TMP_DST, "CVI instruction that doesn't write a register", "", "") DEF_ATTRIB(CVI_SLOT23, "Can execute in slot 2 or slot 3 (HVX)", "", "") +DEF_ATTRIB(CVI_VA_2SRC, "Execs on multimedia vector engine; requires two srcs", "", "") DEF_ATTRIB(VTCM_ALLBANK_ACCESS, "Allocates in all VTCM schedulers.", "", "") @@ -202,6 +203,7 @@ DEF_ATTRIB(HVX_IEEE_FP_ACC, "HVX IEEE FP accumulate instruction", "", "") DEF_ATTRIB(HVX_IEEE_FP_OUT_16, "HVX IEEE FP 16-bit output", "", "") DEF_ATTRIB(HVX_IEEE_FP_OUT_32, "HVX IEEE FP 32-bit output", "", "") DEF_ATTRIB(CVI_VX_NO_TMP_LD, "HVX multiply without tmp load", "", "") +DEF_ATTRIB(HVX_FLT, "This a floating point HVX instruction.", "", "") /* Keep this as the last attribute: */ DEF_ATTRIB(ZZ_LASTATTRIB, "Last attribute in the file", "", "") diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py index 602f49e1f5..e33d43e3ce 100755 --- a/target/hexagon/hex_common.py +++ b/target/hexagon/hex_common.py @@ -254,6 +254,7 @@ def need_env(tag): "A_CVI_GATHER" in attribdict[tag] or "A_CVI_SCATTER" in attribdict[tag] or "A_HVX_IEEE_FP" in attribdict[tag] or + "A_HVX_FLT" in attribdict[tag] or "A_IMPLICIT_WRITES_USR" in attribdict[tag] or "A_PRIV" in attribdict[tag] or "J2_trap" in tag) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index 4ce87d09fd..d7f50db778 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -823,4 +823,14 @@ DEF_ENC(V6_vsub_sf_hf,"00011111100vvvvvPP1uuuuu101ddddd") DEF_ENC(V6_vadd_hf_hf,"00011111101vvvvvPP1uuuuu111ddddd") DEF_ENC(V6_vsub_hf_hf,"00011111011vvvvvPP1uuuuu000ddddd") +/* IEEE FP min/max instructions */ +DEF_ENC(V6_vfmin_hf,"00011100011vvvvvPP1uuuuu000ddddd") +DEF_ENC(V6_vfmin_sf,"00011100011vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vfmax_hf,"00011100011vvvvvPP1uuuuu010ddddd") +DEF_ENC(V6_vfmax_sf,"00011100011vvvvvPP1uuuuu011ddddd") +DEF_ENC(V6_vmax_sf,"00011111110vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vmin_sf,"00011111110vvvvvPP1uuuuu010ddddd") +DEF_ENC(V6_vmax_hf,"00011111110vvvvvPP1uuuuu011ddddd") +DEF_ENC(V6_vmin_hf,"00011111110vvvvvPP1uuuuu100ddddd") + #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 14df8e4790..0e9cace203 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -43,7 +43,9 @@ EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA), \ DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) - +#define ITERATOR_INSN_ANY_SLOT_2SRC(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) #define ITERATOR_INSN2_ANY_SLOT(WIDTH,TAG,SYNTAX,SYNTAX2,DESCR,CODE) \ ITERATOR_INSN_ANY_SLOT(WIDTH,TAG,SYNTAX2,DESCR,CODE) @@ -3000,6 +3002,38 @@ ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vsub_sf_hf, VddV.v[1].sf[i] = float32_sub(f16_to_f32(VuV.hf[2*i+1]), f16_to_f32(VvV.hf[2*i+1]), &env->hvx_fp_status)) +#define ITERATOR_INSN_IEEE_FP_16_32_LATE(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16,A_HVX_IEEE_FP_OUT_32), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* IEEE FP min/max instructions */ +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vfmin_hf, "Vd32.hf=vfmin(Vu32.hf,Vv32.hf)", \ + "Vector IEEE min: hf", VdV.hf[i] = float16_min(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vfmin_sf, "Vd32.sf=vfmin(Vu32.sf,Vv32.sf)", \ + "Vector IEEE min: sf", VdV.sf[i] = float32_min(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vfmax_hf, "Vd32.hf=vfmax(Vu32.hf,Vv32.hf)", \ + "Vector IEEE max: hf", VdV.hf[i] = float16_max(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vfmax_sf, "Vd32.sf=vfmax(Vu32.sf,Vv32.sf)", \ + "Vector IEEE max: sf", VdV.sf[i] = float32_max(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) + +ITERATOR_INSN_ANY_SLOT_2SRC(32,vmax_sf,"Vd32.sf=vmax(Vu32.sf,Vv32.sf)", \ + "Vector max of sf input", VdV.sf[i] = qf_max_sf(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_ANY_SLOT_2SRC(32,vmin_sf,"Vd32.sf=vmin(Vu32.sf,Vv32.sf)", \ + "Vector min of sf input", VdV.sf[i] = qf_min_sf(VuV.sf[i], VvV.sf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_ANY_SLOT_2SRC(16,vmax_hf,"Vd32.hf=vmax(Vu32.hf,Vv32.hf)", \ + "Vector max of hf input", VdV.hf[i] = qf_max_hf(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) +ITERATOR_INSN_ANY_SLOT_2SRC(16,vmin_hf,"Vd32.hf=vmin(Vu32.hf,Vv32.hf)", \ + "Vector min of hf input", VdV.hf[i] = qf_min_hf(VuV.hf[i], VvV.hf[i], \ + &env->hvx_fp_status)) + /****************************************************************************** DEBUG Vector/Register Printing ******************************************************************************/ diff --git a/target/hexagon/mmvec/hvx_ieee_fp.c b/target/hexagon/mmvec/hvx_ieee_fp.c index 3367226998..2ae79a485a 100644 --- a/target/hexagon/mmvec/hvx_ieee_fp.c +++ b/target/hexagon/mmvec/hvx_ieee_fp.c @@ -19,3 +19,53 @@ float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, return float32_add(fp_mult_sf_hf(a1, a3, fp_status), fp_mult_sf_hf(a2, a4, fp_status), fp_status); } + +#define float32_is_pos_nan(X) (float32_is_any_nan(X) && !float32_is_neg(X)) +#define float32_is_neg_nan(X) (float32_is_any_nan(X) && float32_is_neg(X)) +#define float16_is_pos_nan(X) (float16_is_any_nan(X) && !float16_is_neg(X)) +#define float16_is_neg_nan(X) (float16_is_any_nan(X) && float16_is_neg(X)) + +/* Qfloat min/max treat +NaN as greater than +INF and -NaN as smaller than -INF */ +float32 qf_max_sf(float32 a1, float32 a2, float_status *fp_status) +{ + if (float32_is_pos_nan(a1) || float32_is_neg_nan(a2)) { + return a1; + } + if (float32_is_pos_nan(a2) || float32_is_neg_nan(a1)) { + return a2; + } + return float32_max(a1, a2, fp_status); +} + +float32 qf_min_sf(float32 a1, float32 a2, float_status *fp_status) +{ + if (float32_is_pos_nan(a1) || float32_is_neg_nan(a2)) { + return a2; + } + if (float32_is_pos_nan(a2) || float32_is_neg_nan(a1)) { + return a1; + } + return float32_min(a1, a2, fp_status); +} + +float16 qf_max_hf(float16 a1, float16 a2, float_status *fp_status) +{ + if (float16_is_pos_nan(a1) || float16_is_neg_nan(a2)) { + return a1; + } + if (float16_is_pos_nan(a2) || float16_is_neg_nan(a1)) { + return a2; + } + return float16_max(a1, a2, fp_status); +} + +float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status) +{ + if (float16_is_pos_nan(a1) || float16_is_neg_nan(a2)) { + return a2; + } + if (float16_is_pos_nan(a2) || float16_is_neg_nan(a1)) { + return a1; + } + return float16_min(a1, a2, fp_status); +} diff --git a/target/hexagon/mmvec/hvx_ieee_fp.h b/target/hexagon/mmvec/hvx_ieee_fp.h index 75008deb3b..dff2fab14c 100644 --- a/target/hexagon/mmvec/hvx_ieee_fp.h +++ b/target/hexagon/mmvec/hvx_ieee_fp.h @@ -15,4 +15,10 @@ float32 fp_mult_sf_hf(float16 a1, float16 a2, float_status *fp_status); float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, float_status *fp_status); +/* Qfloat min/max treat +NaN as greater than +INF and -NaN as smaller than -INF */ +float32 qf_max_sf(float32 a1, float32 a2, float_status *fp_status); +float32 qf_min_sf(float32 a1, float32 a2, float_status *fp_status); +float16 qf_max_hf(float16 a1, float16 a2, float_status *fp_status); +float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status); + #endif From 60f11988ebd6a2a44fff8230aa41ad384aac1f97 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:57 -0700 Subject: [PATCH 428/860] target/hexagon: add v68 HVX IEEE float misc insns Add HVX IEEE floating-point miscellaneous instructions: - vassign_fp (vfmv): vector move - vfneg_hf, vfneg_sf: vector floating-point negate - vabs_hf, vabs_sf: vector absolute value Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/0bd3279ca0bbf7c01548e3b4e7a9c8fdbdbe4ee1.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/imported/mmvec/encode_ext.def | 7 +++++++ target/hexagon/imported/mmvec/ext.idef | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index d7f50db778..72daf8724c 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -833,4 +833,11 @@ DEF_ENC(V6_vmin_sf,"00011111110vvvvvPP1uuuuu010ddddd") DEF_ENC(V6_vmax_hf,"00011111110vvvvvPP1uuuuu011ddddd") DEF_ENC(V6_vmin_hf,"00011111110vvvvvPP1uuuuu100ddddd") +/* IEEE FP move, negate, abs instructions */ +DEF_ENC(V6_vassign_fp,"00011110--0-0110PP1uuuuu001ddddd") +DEF_ENC(V6_vfneg_hf,"00011110--0-0110PP1uuuuu010ddddd") +DEF_ENC(V6_vfneg_sf,"00011110--0-0110PP1uuuuu011ddddd") +DEF_ENC(V6_vabs_hf,"00011110--0-0110PP1uuuuu100ddddd") +DEF_ENC(V6_vabs_sf,"00011110--0-0110PP1uuuuu101ddddd") + #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 0e9cace203..1b16ed0628 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -3034,6 +3034,18 @@ ITERATOR_INSN_ANY_SLOT_2SRC(16,vmin_hf,"Vd32.hf=vmin(Vu32.hf,Vv32.hf)", \ "Vector min of hf input", VdV.hf[i] = qf_min_hf(VuV.hf[i], VvV.hf[i], \ &env->hvx_fp_status)) +/* IEEE FP move, negate, abs instructions */ +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vassign_fp, "Vd32.w=vfmv(Vu32.w)", \ + "Vector IEEE move", VdV.w[i] = VuV.w[i]) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vfneg_hf, "Vd32.hf=vfneg(Vu32.hf)", \ + "Vector IEEE neg: hf", VdV.hf[i] = float16_chs(VuV.hf[i])) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vfneg_sf, "Vd32.sf=vfneg(Vu32.sf)", \ + "Vector IEEE neg: sf", VdV.sf[i] = float32_chs(VuV.sf[i])) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vabs_hf, "Vd32.hf=vabs(Vu32.hf)", \ + "Vector IEEE abs: hf", VdV.hf[i] = float16_abs(VuV.hf[i])) +ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vabs_sf, "Vd32.sf=vabs(Vu32.sf)", \ + "Vector IEEE abs: sf", VdV.sf[i] = float32_abs(VuV.sf[i])) + /****************************************************************************** DEBUG Vector/Register Printing ******************************************************************************/ From 011b2512f9551b9c91d760fc38d65e5eb7bc2b3b Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:58 -0700 Subject: [PATCH 429/860] target/hexagon: add v68 HVX IEEE float conversion insns Add HVX IEEE floating-point conversion instructions: - vconv_hf_h, vconv_h_hf, vconv_sf_w, vconv_w_sf: vconv operations - vcvt_hf_sf, vcvt_sf_hf: float <-> half float conversions - vcvt_hf_b, vcvt_hf_h, vcvt_hf_ub, vcvt_hf_uh: int to half float - vcvt_b_hf, vcvt_h_hf, vcvt_ub_hf, vcvt_uh_hf: half float to int Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/beca620d2e5e0bc15886bf967ab3961a49eca5f7.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/imported/mmvec/encode_ext.def | 18 ++++ target/hexagon/imported/mmvec/ext.idef | 97 ++++++++++++++++++++ target/hexagon/mmvec/hvx_ieee_fp.c | 18 ++++ target/hexagon/mmvec/hvx_ieee_fp.h | 4 + 4 files changed, 137 insertions(+) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index 72daf8724c..c1ed1b6c23 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -840,4 +840,22 @@ DEF_ENC(V6_vfneg_sf,"00011110--0-0110PP1uuuuu011ddddd") DEF_ENC(V6_vabs_hf,"00011110--0-0110PP1uuuuu100ddddd") DEF_ENC(V6_vabs_sf,"00011110--0-0110PP1uuuuu101ddddd") +/* IEEE FP vcvt instructions */ +DEF_ENC(V6_vcvt_sf_hf,"00011110--0-0100PP1uuuuu100ddddd") +DEF_ENC(V6_vcvt_hf_sf,"00011111011vvvvvPP1uuuuu001ddddd") +DEF_ENC(V6_vcvt_hf_ub,"00011110--0-0100PP1uuuuu001ddddd") +DEF_ENC(V6_vcvt_hf_b,"00011110--0-0100PP1uuuuu010ddddd") +DEF_ENC(V6_vcvt_hf_uh,"00011110--0-0100PP1uuuuu101ddddd") +DEF_ENC(V6_vcvt_hf_h,"00011110--0-0100PP1uuuuu111ddddd") +DEF_ENC(V6_vcvt_uh_hf,"00011110--0--101PP1uuuuu000ddddd") +DEF_ENC(V6_vcvt_h_hf,"00011110--0-0110PP1uuuuu000ddddd") +DEF_ENC(V6_vcvt_ub_hf,"00011111110vvvvvPP1uuuuu101ddddd") +DEF_ENC(V6_vcvt_b_hf,"00011111110vvvvvPP1uuuuu110ddddd") + +/* IEEE FP vconv instructions */ +DEF_ENC(V6_vconv_sf_w,"00011110--0--101PP1uuuuu011ddddd") +DEF_ENC(V6_vconv_w_sf,"00011110--0--101PP1uuuuu001ddddd") +DEF_ENC(V6_vconv_hf_h,"00011110--0--101PP1uuuuu100ddddd") +DEF_ENC(V6_vconv_h_hf,"00011110--0--101PP1uuuuu010ddddd") + #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 1b16ed0628..788ce1d2ae 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -63,6 +63,9 @@ ITERATOR_INSN_ANY_SLOT_DOUBLE_VEC(WIDTH,TAG,SYNTAX2,DESCR,CODE) EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS), \ DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) +#define ITERATOR_INSN_SHIFT_SLOT_FLT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_HVX_FLT), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) #define ITERATOR_INSN_SHIFT3_SLOT(WIDTH,TAG,SYNTAX,DESCR,CODE) \ EXTINSN(V6_##TAG, SYNTAX, ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VS,A_CVI_VS_3SRC,A_NOTE_SHIFT_RESOURCE,A_NOTE_NOVP,A_NOTE_VA_UNARY), \ @@ -3046,6 +3049,100 @@ ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vabs_hf, "Vd32.hf=vabs(Vu32.hf)", \ ITERATOR_INSN_IEEE_FP_16_32_LATE(32, vabs_sf, "Vd32.sf=vabs(Vu32.sf)", \ "Vector IEEE abs: sf", VdV.sf[i] = float32_abs(VuV.sf[i])) +/* Two pipes: P2 & P3, two outputs, 16-bit */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ +ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_16), \ +DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* Two pipes: P2 & P3, two outputs, 32-bit output */ +#define ITERATOR_INSN_IEEE_FP_DOUBLE_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX_DV,A_HVX_IEEE_FP_OUT_32), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* Single pipe, 16-bit output */ +#define ITERATOR_INSN_IEEE_FP_16(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/* single pipe, output can feed 16- or 32-bit accumulate */ +#define ITERATOR_INSN_IEEE_FP_16_32(WIDTH,TAG,SYNTAX,DESCR,CODE) \ +EXTINSN(V6_##TAG, SYNTAX, \ + ATTRIBS(A_EXTENSION,A_HVX_IEEE_FP,A_CVI,A_CVI_VX,A_HVX_IEEE_FP_OUT_16,A_HVX_IEEE_FP_OUT_32), \ + DESCR, DO_FOR_EACH_CODE(WIDTH, CODE)) + +/****************************************************************************** + * IEEE FP convert instructions + ******************************************************************************/ + +ITERATOR_INSN_IEEE_FP_DOUBLE_16(32, vcvt_hf_ub, "Vdd32.hf=vcvt(Vu32.ub)", + "Vector IEEE cvt from int: ub widen to hf", + VddV.v[0].hf[2*i] = uint64_to_float16_scalbn(VuV.ub[4*i], float_round_nearest_even, 0); + VddV.v[0].hf[2*i+1] = uint64_to_float16_scalbn(VuV.ub[4*i+1], float_round_nearest_even, 0); + VddV.v[1].hf[2*i] = uint64_to_float16_scalbn(VuV.ub[4*i+2], float_round_nearest_even, 0); + VddV.v[1].hf[2*i+1] = uint64_to_float16_scalbn(VuV.ub[4*i+3], float_round_nearest_even, 0)) + +ITERATOR_INSN_IEEE_FP_DOUBLE_16(32, vcvt_hf_b, "Vdd32.hf=vcvt(Vu32.b)", + "Vector IEEE cvt from int: b widen to hf", + VddV.v[0].hf[2*i] = int64_to_float16_scalbn(VuV.b[4*i], float_round_nearest_even, 0); + VddV.v[0].hf[2*i+1] = int64_to_float16_scalbn(VuV.b[4*i+1], float_round_nearest_even, 0); + VddV.v[1].hf[2*i] = int64_to_float16_scalbn(VuV.b[4*i+2], float_round_nearest_even, 0); + VddV.v[1].hf[2*i+1] = int64_to_float16_scalbn(VuV.b[4*i+3], float_round_nearest_even, 0)) + +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vcvt_sf_hf, "Vdd32.sf=vcvt(Vu32.hf)", + "Vector IEEE cvt: hf widen to sf", + VddV.v[0].sf[i] = f16_to_f32(VuV.hf[2*i]); + VddV.v[1].sf[i] = f16_to_f32(VuV.hf[2*i+1])) + +ITERATOR_INSN_IEEE_FP_16(16, vcvt_hf_uh, "Vd32.hf=vcvt(Vu32.uh)", + "Vector IEEE cvt from int: uh to hf", + VdV.hf[i] = uint64_to_float16_scalbn(VuV.uh[i], float_round_nearest_even, 0)) +ITERATOR_INSN_IEEE_FP_16(16, vcvt_hf_h, "Vd32.hf=vcvt(Vu32.h)", + "Vector IEEE cvt from int: h to hf", + VdV.hf[i] = int64_to_float16_scalbn(VuV.h[i], float_round_nearest_even, 0)) +ITERATOR_INSN_IEEE_FP_16_32(16, vcvt_uh_hf, "Vd32.uh=vcvt(Vu32.hf)", + "Vector IEEE cvt to int: hf to uh", + VdV.uh[i] = float16_to_uint16_scalbn(VuV.hf[i], float_round_nearest_even, 0, &env->hvx_fp_status)) +ITERATOR_INSN_IEEE_FP_16_32(16, vcvt_h_hf, "Vd32.h=vcvt(Vu32.hf)", + "Vector IEEE cvt to int: hf to h", + VdV.h[i] = float16_to_int16_scalbn(VuV.hf[i], float_round_nearest_even, 0, &env->hvx_fp_status)) + +ITERATOR_INSN_IEEE_FP_16(32, vcvt_hf_sf, "Vd32.hf=vcvt(Vu32.sf,Vv32.sf)", + "Vector IEEE cvt: sf to hf", + VdV.hf[2*i] = f32_to_f16(VuV.sf[i]); + VdV.hf[2*i+1] = f32_to_f16(VvV.sf[i])) + +ITERATOR_INSN_IEEE_FP_16_32(32, vcvt_ub_hf, "Vd32.ub=vcvt(Vu32.hf,Vv32.hf)", "Vector cvt to int: hf narrow to ub", + VdV.ub[4*i] = float16_to_uint8_scalbn(VuV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.ub[4*i+1] = float16_to_uint8_scalbn(VuV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.ub[4*i+2] = float16_to_uint8_scalbn(VvV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.ub[4*i+3] = float16_to_uint8_scalbn(VvV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status)) + +ITERATOR_INSN_IEEE_FP_16_32(32, vcvt_b_hf, "Vd32.b=vcvt(Vu32.hf,Vv32.hf)", + "Vector cvt to int: hf narrow to b", + VdV.b[4*i] = float16_to_int8_scalbn(VuV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.b[4*i+1] = float16_to_int8_scalbn(VuV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.b[4*i+2] = float16_to_int8_scalbn(VvV.hf[2*i], float_round_nearest_even, 0, &env->hvx_fp_status); + VdV.b[4*i+3] = float16_to_int8_scalbn(VvV.hf[2*i+1], float_round_nearest_even, 0, &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(32, vconv_w_sf,"Vd32.w=Vu32.sf", + "Vector conversion of sf32 format to int w", + VdV.w[i] = conv_w_sf(VuV.sf[i], &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_h_hf,"Vd32.h=Vu32.hf", + "Vector conversion of hf16 format to int hw", + VdV.h[i] = conv_h_hf(VuV.hf[i], &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(32, vconv_sf_w,"Vd32.sf=Vu32.w", + "Vector conversion of int w format to sf32", + VdV.sf[i] = int32_to_float32(VuV.w[i], &env->hvx_fp_status)) + +ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_hf_h,"Vd32.hf=Vu32.h", + "Vector conversion of int hw format to hf16", + VdV.hf[i] = float16_val(int16_to_float16(VuV.h[i], &env->hvx_fp_status))) + /****************************************************************************** DEBUG Vector/Register Printing ******************************************************************************/ diff --git a/target/hexagon/mmvec/hvx_ieee_fp.c b/target/hexagon/mmvec/hvx_ieee_fp.c index 2ae79a485a..697f35b5ed 100644 --- a/target/hexagon/mmvec/hvx_ieee_fp.c +++ b/target/hexagon/mmvec/hvx_ieee_fp.c @@ -69,3 +69,21 @@ float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status) } return float16_min(a1, a2, fp_status); } + +int32_t conv_w_sf(float32 a, float_status *fp_status) +{ + /* float32_to_int32 converts any NaN to MAX, hexagon looks at the sign. */ + if (float32_is_any_nan(a)) { + return float32_is_neg(a) ? INT32_MIN : INT32_MAX; + } + return float32_to_int32_round_to_zero(a, fp_status); +} + +int16_t conv_h_hf(float16 a, float_status *fp_status) +{ + /* float16_to_int16 converts any NaN to MAX, hexagon looks at the sign. */ + if (float16_is_any_nan(a)) { + return float16_is_neg(a) ? INT16_MIN : INT16_MAX; + } + return float16_to_int16_round_to_zero(a, fp_status); +} diff --git a/target/hexagon/mmvec/hvx_ieee_fp.h b/target/hexagon/mmvec/hvx_ieee_fp.h index dff2fab14c..bdc21e08f0 100644 --- a/target/hexagon/mmvec/hvx_ieee_fp.h +++ b/target/hexagon/mmvec/hvx_ieee_fp.h @@ -10,6 +10,7 @@ #include "fpu/softfloat.h" #define f16_to_f32(A) float16_to_float32((A), true, &env->hvx_fp_status) +#define f32_to_f16(A) float32_to_float16((A), true, &env->hvx_fp_status) float32 fp_mult_sf_hf(float16 a1, float16 a2, float_status *fp_status); float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, @@ -21,4 +22,7 @@ float32 qf_min_sf(float32 a1, float32 a2, float_status *fp_status); float16 qf_max_hf(float16 a1, float16 a2, float_status *fp_status); float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status); +int32_t conv_w_sf(float32 a, float_status *fp_status); +int16_t conv_h_hf(float16 a, float_status *fp_status); + #endif From 7646bc0b72a78960d497371036d5841dc590412b Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:38:59 -0700 Subject: [PATCH 430/860] target/hexagon: add v68 HVX IEEE float compare insns Add HVX IEEE floating-point compare instructions: - V6_vgthf, V6_vgtsf: greater-than compare - V6_vgthf_and, V6_vgtsf_and: greater-than with predicate-and - V6_vgthf_or, V6_vgtsf_or: greater-than with predicate-or - V6_vgthf_xor, V6_vgtsf_xor: greater-than with predicate-xor Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/96e27588ad5a7d47ba9f56f1d547729a19b691c5.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/imported/mmvec/encode_ext.def | 10 ++++ target/hexagon/imported/mmvec/ext.idef | 61 ++++++++++++++++++++ target/hexagon/mmvec/hvx_ieee_fp.c | 48 +++++++++++++++ target/hexagon/mmvec/hvx_ieee_fp.h | 4 ++ target/hexagon/mmvec/macros.h | 6 +- 5 files changed, 128 insertions(+), 1 deletion(-) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index c1ed1b6c23..3572e4de4c 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -858,4 +858,14 @@ DEF_ENC(V6_vconv_w_sf,"00011110--0--101PP1uuuuu001ddddd") DEF_ENC(V6_vconv_hf_h,"00011110--0--101PP1uuuuu100ddddd") DEF_ENC(V6_vconv_h_hf,"00011110--0--101PP1uuuuu010ddddd") +/* IEEE FP compare instructions */ +DEF_ENC(V6_vgtsf,"00011100100vvvvvPP1uuuuu011100dd") +DEF_ENC(V6_vgthf,"00011100100vvvvvPP1uuuuu011101dd") +DEF_ENC(V6_vgtsf_and,"00011100100vvvvvPP1uuuuu110010xx") +DEF_ENC(V6_vgthf_and,"00011100100vvvvvPP1uuuuu110011xx") +DEF_ENC(V6_vgtsf_or,"00011100100vvvvvPP1uuuuu001100xx") +DEF_ENC(V6_vgthf_or,"00011100100vvvvvPP1uuuuu001101xx") +DEF_ENC(V6_vgtsf_xor,"00011100100vvvvvPP1uuuuu111010xx") +DEF_ENC(V6_vgthf_xor,"00011100100vvvvvPP1uuuuu111011xx") + #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index 788ce1d2ae..a7043d598c 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -3143,6 +3143,67 @@ ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_hf_h,"Vd32.hf=Vu32.h", "Vector conversion of int hw format to hf16", VdV.hf[i] = float16_val(int16_to_float16(VuV.h[i], &env->hvx_fp_status))) +/****************************************************************************** + * IEEE FP compare instructions + ******************************************************************************/ + +#define VCMPGT_SF(DEST, ASRC, ASRCOP, CMP, N, SRC, MASK, WIDTH) \ +{ \ + for (fHIDE(int) i = 0; i < fVBYTES(); i += WIDTH) { \ + fHIDE(int) VAL = fCMPGT_SF(VuV.SRC[i/WIDTH],VvV.SRC[i/WIDTH]) ? MASK : 0; \ + fSETQBITS(DEST,WIDTH,MASK,i,ASRC ASRCOP VAL); \ + } \ +} + +#define VCMPGT_HF(DEST, ASRC, ASRCOP, CMP, N, SRC, MASK, WIDTH) \ +{ \ + for (fHIDE(int) i = 0; i < fVBYTES(); i += WIDTH) { \ + fHIDE(int) VAL = fCMPGT_HF(VuV.SRC[i/WIDTH],VvV.SRC[i/WIDTH]) ? MASK : 0; \ + fSETQBITS(DEST,WIDTH,MASK,i,ASRC ASRCOP VAL); \ + } \ +} + +/* Vector SF compare */ +#define MMVEC_CMPGT_SF(TYPE,TYPE2,DESCR,N,MASK,WIDTH,SRC) \ + EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-and", \ + VCMPGT_SF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_xor, "Qx4^=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-xor", \ + VCMPGT_SF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_or, "Qx4|=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-or", \ + VCMPGT_SF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE, "Qd4=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than", \ + VCMPGT_SF(QdV, , , ">", N, SRC, MASK, WIDTH)) + +/* Vector HF compare */ +#define MMVEC_CMPGT_HF(TYPE,TYPE2,DESCR,N,MASK,WIDTH,SRC) \ + EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-and", \ + VCMPGT_HF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_xor, "Qx4^=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-xor", \ + VCMPGT_HF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_or, "Qx4|=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-or", \ + VCMPGT_HF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE, "Qd4=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than", \ + VCMPGT_HF(QdV, , , ">", N, SRC, MASK, WIDTH)) + +MMVEC_CMPGT_SF(sf,"sf","Vector sf Compare ", fVELEM(32), 0xF, 4, sf) +MMVEC_CMPGT_HF(hf,"hf","Vector hf Compare ", fVELEM(16), 0x3, 2, hf) + /****************************************************************************** DEBUG Vector/Register Printing ******************************************************************************/ diff --git a/target/hexagon/mmvec/hvx_ieee_fp.c b/target/hexagon/mmvec/hvx_ieee_fp.c index 697f35b5ed..d7751adbe2 100644 --- a/target/hexagon/mmvec/hvx_ieee_fp.c +++ b/target/hexagon/mmvec/hvx_ieee_fp.c @@ -87,3 +87,51 @@ int16_t conv_h_hf(float16 a, float_status *fp_status) } return float16_to_int16_round_to_zero(a, fp_status); } + +/* + * Returns true if f1 > f2, where at least one of the elements is guaranteed + * to be NaN. + * Up to v73, Hexagon HVX IEEE FP follows this order: + * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg + */ +static bool float32_nan_compare(float32 f1, float32 f2, float_status *fp_status) +{ + /* opposite signs case */ + if (float32_is_neg(f1) != float32_is_neg(f2)) { + return !float32_is_neg(f1); + } + + /* same sign case */ + bool result = (float32_is_any_nan(f1) && !float32_is_any_nan(f2)) || + (float32_is_quiet_nan(f1, fp_status) && !float32_is_quiet_nan(f2, fp_status)); + return float32_is_neg(f1) ? !result : result; +} + +static bool float16_nan_compare(float16 f1, float16 f2, float_status *fp_status) +{ + /* opposite signs case */ + if (float16_is_neg(f1) != float16_is_neg(f2)) { + return !float16_is_neg(f1); + } + + /* same sign case */ + bool result = (float16_is_any_nan(f1) && !float16_is_any_nan(f2)) || + (float16_is_quiet_nan(f1, fp_status) && !float16_is_quiet_nan(f2, fp_status)); + return float16_is_neg(f1) ? !result : result; +} + +uint32_t cmpgt_sf(float32 a1, float32 a2, float_status *fp_status) +{ + if (float32_is_any_nan(a1) || float32_is_any_nan(a2)) { + return float32_nan_compare(a1, a2, fp_status); + } + return float32_compare(a1, a2, fp_status) == float_relation_greater; +} + +uint16_t cmpgt_hf(float16 a1, float16 a2, float_status *fp_status) +{ + if (float16_is_any_nan(a1) || float16_is_any_nan(a2)) { + return float16_nan_compare(a1, a2, fp_status); + } + return float16_compare(a1, a2, fp_status) == float_relation_greater; +} diff --git a/target/hexagon/mmvec/hvx_ieee_fp.h b/target/hexagon/mmvec/hvx_ieee_fp.h index bdc21e08f0..01728121eb 100644 --- a/target/hexagon/mmvec/hvx_ieee_fp.h +++ b/target/hexagon/mmvec/hvx_ieee_fp.h @@ -25,4 +25,8 @@ float16 qf_min_hf(float16 a1, float16 a2, float_status *fp_status); int32_t conv_w_sf(float32 a, float_status *fp_status); int16_t conv_h_hf(float16 a, float_status *fp_status); +/* IEEE - FP compare instructions */ +uint32_t cmpgt_sf(float32 a1, float32 a2, float_status *fp_status); +uint16_t cmpgt_hf(float16 a1, float16 a2, float_status *fp_status); + #endif diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index ac709d8993..757fbec5c9 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -354,6 +354,10 @@ do { \ COE = (sextract32(VAL, 24 + 2 * POS, 2) << 8) | \ extract32(VAL, POS * 8, 8); \ - } while (0); + } while (0) \ + ; + +#define fCMPGT_SF(A, B) cmpgt_sf(A, B, &env->hvx_fp_status) +#define fCMPGT_HF(A, B) cmpgt_hf(A, B, &env->hvx_fp_status) #endif From 9d70629fa0e03ee2341f1aab4928d43ed1718d93 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:39:00 -0700 Subject: [PATCH 431/860] target/hexagon: add v73 HVX IEEE bfloat16 insns Add HVX IEEE bfloat16 (bf16) instructions: Arithmetic operations: - V6_vadd_sf_bf, V6_vsub_sf_bf: add/sub bf16 widening to sf output - V6_vmpy_sf_bf: multiply bf16 widening to sf output - V6_vmpy_sf_bf_acc: multiply-accumulate bf16 widening to sf output Min/Max operations: - V6_vmin_bf, V6_vmax_bf: bf16 min/max Comparison operations: - V6_vgtbf: greater-than compare - V6_vgtbf_and, V6_vgtbf_or, V6_vgtbf_xor: predicate variants Conversion operations: - V6_vcvt_bf_sf: convert sf to bf16 Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/1a253373567781e0e141c34b41eaffad4789a493.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- target/hexagon/imported/mmvec/encode_ext.def | 15 +++++ target/hexagon/imported/mmvec/ext.idef | 62 ++++++++++++++++++++ target/hexagon/mmvec/hvx_ieee_fp.h | 37 ++++++++++++ target/hexagon/mmvec/macros.h | 4 ++ target/hexagon/mmvec/mmvec.h | 1 + 5 files changed, 119 insertions(+) diff --git a/target/hexagon/imported/mmvec/encode_ext.def b/target/hexagon/imported/mmvec/encode_ext.def index 3572e4de4c..16f043b77d 100644 --- a/target/hexagon/imported/mmvec/encode_ext.def +++ b/target/hexagon/imported/mmvec/encode_ext.def @@ -868,4 +868,19 @@ DEF_ENC(V6_vgthf_or,"00011100100vvvvvPP1uuuuu001101xx") DEF_ENC(V6_vgtsf_xor,"00011100100vvvvvPP1uuuuu111010xx") DEF_ENC(V6_vgthf_xor,"00011100100vvvvvPP1uuuuu111011xx") +/* BFLOAT instructions */ +DEF_ENC(V6_vmpy_sf_bf,"00011101010vvvvvPP1uuuuu100ddddd") +DEF_ENC(V6_vmpy_sf_bf_acc,"00011101000vvvvvPP1uuuuu000xxxxx") +DEF_ENC(V6_vadd_sf_bf,"00011101010vvvvvPP1uuuuu110ddddd") +DEF_ENC(V6_vsub_sf_bf,"00011101010vvvvvPP1uuuuu101ddddd") +DEF_ENC(V6_vmax_bf,"00011101010vvvvvPP1uuuuu111ddddd") +DEF_ENC(V6_vmin_bf,"00011101010vvvvvPP1uuuuu000ddddd") +DEF_ENC(V6_vcvt_bf_sf,"00011101010vvvvvPP1uuuuu011ddddd") + +/* BFLOAT compare instructions */ +DEF_ENC(V6_vgtbf,"00011100100vvvvvPP1uuuuu011110dd") +DEF_ENC(V6_vgtbf_and,"00011100100vvvvvPP1uuuuu110100xx") +DEF_ENC(V6_vgtbf_or,"00011100100vvvvvPP1uuuuu001110xx") +DEF_ENC(V6_vgtbf_xor,"00011100100vvvvvPP1uuuuu111100xx") + #endif /* NO MMVEC */ diff --git a/target/hexagon/imported/mmvec/ext.idef b/target/hexagon/imported/mmvec/ext.idef index a7043d598c..857aa6133f 100644 --- a/target/hexagon/imported/mmvec/ext.idef +++ b/target/hexagon/imported/mmvec/ext.idef @@ -3163,6 +3163,15 @@ ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_hf_h,"Vd32.hf=Vu32.h", } \ } +#define VCMPGT_BF(DEST, ASRC, ASRCOP, CMP, N, SRC, MASK, WIDTH) \ +{ \ + fBFLOAT(); \ + for (fHIDE(int) i = 0; i < fVBYTES(); i += WIDTH) { \ + fHIDE(int) VAL = fCMPGT_BF(VuV.SRC[i/WIDTH],VvV.SRC[i/WIDTH]) ? MASK : 0; \ + fSETQBITS(DEST,WIDTH,MASK,i,ASRC ASRCOP VAL); \ + } \ +} + /* Vector SF compare */ #define MMVEC_CMPGT_SF(TYPE,TYPE2,DESCR,N,MASK,WIDTH,SRC) \ EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ @@ -3201,8 +3210,61 @@ ITERATOR_INSN_SHIFT_SLOT_FLT(16, vconv_hf_h,"Vd32.hf=Vu32.h", DESCR" greater than", \ VCMPGT_HF(QdV, , , ">", N, SRC, MASK, WIDTH)) +/* Vector BF compare */ +#define MMVEC_CMPGT_BF(TYPE,TYPE2,DESCR,N,MASK,WIDTH,SRC) \ + EXTINSN(V6_vgt##TYPE##_and, "Qx4&=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")",\ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-and", \ + VCMPGT_BF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), &, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_xor, "Qx4^=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-xor", \ + VCMPGT_BF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), ^, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE##_or, "Qx4|=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than with predicate-or", \ + VCMPGT_BF(QxV, fGETQBITS(QxV,WIDTH,MASK,i), |, ">", N, SRC, MASK, WIDTH)) \ + EXTINSN(V6_vgt##TYPE, "Qd4=vcmp.gt(Vu32." TYPE2 ",Vv32." TYPE2 ")", \ + ATTRIBS(A_EXTENSION,A_CVI,A_CVI_VA,A_CVI_VA_2SRC,A_HVX_FLT), \ + DESCR" greater than", \ + VCMPGT_BF(QdV, , , ">", N, SRC, MASK, WIDTH)) + MMVEC_CMPGT_SF(sf,"sf","Vector sf Compare ", fVELEM(32), 0xF, 4, sf) MMVEC_CMPGT_HF(hf,"hf","Vector hf Compare ", fVELEM(16), 0x3, 2, hf) +MMVEC_CMPGT_BF(bf,"bf","Vector bf Compare ", fVELEM(16), 0x3, 2, bf) + +/****************************************************************************** + BFloat arithmetic and max/min instructions + ******************************************************************************/ + +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vadd_sf_bf, + "Vdd32.sf=vadd(Vu32.bf,Vv32.bf)", "Vector IEEE add: bf widen to sf", + VddV.v[0].sf[i] = fp_add_sf_bf(VuV.bf[2*i], VvV.bf[2*i]); + VddV.v[1].sf[i] = fp_add_sf_bf(VuV.bf[2*i+1], VvV.bf[2*i+1]); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vsub_sf_bf, + "Vdd32.sf=vsub(Vu32.bf,Vv32.bf)", "Vector IEEE sub: bf widen to sf", + VddV.v[0].sf[i] = fp_sub_sf_bf(VuV.bf[2*i], VvV.bf[2*i]); + VddV.v[1].sf[i] = fp_sub_sf_bf(VuV.bf[2*i+1], VvV.bf[2*i+1]); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_bf, + "Vdd32.sf=vmpy(Vu32.bf,Vv32.bf)", "Vector IEEE mul: hf widen to sf", + VddV.v[0].sf[i] = fp_mult_sf_bf(VuV.bf[2*i], VvV.bf[2*i]); + VddV.v[1].sf[i] = fp_mult_sf_bf(VuV.bf[2*i+1], VvV.bf[2*i+1]); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_DOUBLE_32(32, vmpy_sf_bf_acc, + "Vxx32.sf+=vmpy(Vu32.bf,Vv32.bf)", "Vector IEEE fma: hf widen to sf", + VxxV.v[0].sf[i] = fp_mult_sf_bf_acc(VuV.bf[2*i], VvV.bf[2*i], VxxV.v[0].sf[i]); + VxxV.v[1].sf[i] = fp_mult_sf_bf_acc(VuV.bf[2*i+1], VvV.bf[2*i+1], VxxV.v[1].sf[i]); + fCVI_VX_NO_TMP_LD(); fBFLOAT()) +ITERATOR_INSN_IEEE_FP_16(32, vcvt_bf_sf, + "Vd32.bf=vcvt(Vu32.sf,Vv32.sf)", "Vector IEEE cvt: sf to bf", + VdV.bf[2*i] = f32_to_bf16(VuV.sf[i], &env->hvx_fp_status); + VdV.bf[2*i+1] = f32_to_bf16(VvV.sf[i], &env->hvx_fp_status); fBFLOAT()) + +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vmax_bf, "Vd32.bf=vmax(Vu32.bf,Vv32.bf)", + "Vector IEEE max: bf", VdV.bf[i] = fp_max_bf(VuV.bf[i], VvV.bf[i]); + fBFLOAT()) +ITERATOR_INSN_IEEE_FP_16_32_LATE(16, vmin_bf, "Vd32.bf=vmin(Vu32.bf,Vv32.bf)", + "Vector IEEE min: bf", VdV.bf[i] = fp_min_bf(VuV.bf[i], VvV.bf[i]); + fBFLOAT()) /****************************************************************************** DEBUG Vector/Register Printing diff --git a/target/hexagon/mmvec/hvx_ieee_fp.h b/target/hexagon/mmvec/hvx_ieee_fp.h index 01728121eb..b7e379b089 100644 --- a/target/hexagon/mmvec/hvx_ieee_fp.h +++ b/target/hexagon/mmvec/hvx_ieee_fp.h @@ -9,8 +9,11 @@ #include "fpu/softfloat.h" +#define FP32_DEF_NAN 0x7FFFFFFF + #define f16_to_f32(A) float16_to_float32((A), true, &env->hvx_fp_status) #define f32_to_f16(A) float32_to_float16((A), true, &env->hvx_fp_status) +#define bf16_to_f32(A) bfloat16_to_float32(A, &env->hvx_fp_status) float32 fp_mult_sf_hf(float16 a1, float16 a2, float_status *fp_status); float32 fp_vdmpy(float16 a1, float16 a2, float16 a3, float16 a4, @@ -29,4 +32,38 @@ int16_t conv_h_hf(float16 a, float_status *fp_status); uint32_t cmpgt_sf(float32 a1, float32 a2, float_status *fp_status); uint16_t cmpgt_hf(float16 a1, float16 a2, float_status *fp_status); +/* IEEE BFloat instructions */ + +#define fp_mult_sf_bf(A, B) \ + float32_mul(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status) + +#define fp_add_sf_bf(A, B) \ + float32_add(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status) + +#define fp_sub_sf_bf(A, B) \ + float32_sub(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status) + +#define fp_mult_sf_bf_acc(f1, f2, f3) \ + float32_muladd(bf16_to_f32(f1), bf16_to_f32(f2), f3, 0, &env->hvx_fp_status) + +static inline bfloat16 f32_to_bf16(float32 A, float_status *fp_status) +{ + uint32_t rslt = A; + if ((rslt & 0x1FFFF) == 0x08000) { + /* do not round up if exactly .5 and even already */ + } else if ((rslt & 0x8000) == 0x8000) { + rslt += 0x8000; /* rounding to nearest number */ + } + rslt = float32_is_any_nan(A) ? FP32_DEF_NAN : rslt; + return float32_to_bfloat16(rslt, fp_status); +} + +#define fp_min_bf(A, B) \ + f32_to_bf16(float32_min(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status), \ + &env->hvx_fp_status); + +#define fp_max_bf(A, B) \ + f32_to_bf16(float32_max(bf16_to_f32(A), bf16_to_f32(B), &env->hvx_fp_status), \ + &env->hvx_fp_status); + #endif diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index 757fbec5c9..b36f833b1d 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -25,6 +25,9 @@ #include "accel/tcg/probe.h" #include "mmvec/hvx_ieee_fp.h" +#define fBFLOAT() +#define fCVI_VX_NO_TMP_LD() + #ifndef QEMU_GENERATE #define VdV (*(MMVector *restrict)(VdV_void)) #define VsV (*(MMVector *restrict)(VsV_void)) @@ -359,5 +362,6 @@ #define fCMPGT_SF(A, B) cmpgt_sf(A, B, &env->hvx_fp_status) #define fCMPGT_HF(A, B) cmpgt_hf(A, B, &env->hvx_fp_status) +#define fCMPGT_BF(A, B) fCMPGT_SF((uint32_t)(A) << 16, (uint32_t)(B) << 16) #endif diff --git a/target/hexagon/mmvec/mmvec.h b/target/hexagon/mmvec/mmvec.h index 9841687f83..8e72f2f6ae 100644 --- a/target/hexagon/mmvec/mmvec.h +++ b/target/hexagon/mmvec/mmvec.h @@ -43,6 +43,7 @@ typedef union { int8_t b[MAX_VEC_SIZE_BYTES / 1]; float32 sf[MAX_VEC_SIZE_BYTES / 4]; float16 hf[MAX_VEC_SIZE_BYTES / 2]; + bfloat16 bf[MAX_VEC_SIZE_BYTES / 2]; } MMVector; typedef union { From 449e485814ddadb1ae6f50ca9c9e01a248985eae Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:39:01 -0700 Subject: [PATCH 432/860] tests/hexagon: add tests for v68 HVX IEEE float arithmetics Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/791f122bfd744a77177608b524d852fe826cd595.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/tcg/hexagon/Makefile.target | 8 ++ tests/tcg/hexagon/fp_hvx.c | 155 ++++++++++++++++++++++++++++ tests/tcg/hexagon/fp_hvx_disabled.c | 57 ++++++++++ tests/tcg/hexagon/hex_test.h | 4 + tests/tcg/hexagon/hvx_misc.h | 41 ++++++++ 5 files changed, 265 insertions(+) create mode 100644 tests/tcg/hexagon/fp_hvx.c create mode 100644 tests/tcg/hexagon/fp_hvx_disabled.c diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 80e56f7d05..2a5958711b 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -50,6 +50,8 @@ HEX_TESTS += vector_add_int HEX_TESTS += scatter_gather HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram +HEX_TESTS += fp_hvx +HEX_TESTS += fp_hvx_disabled HEX_TESTS += invalid-slots HEX_TESTS += valid-slots HEX_TESTS += invalid-encoding @@ -136,6 +138,12 @@ v68_hvx: CFLAGS += -mhvx -Wno-unused-function v69_hvx: v69_hvx.c hvx_misc.h v69_hvx: CFLAGS += -mhvx -Wno-unused-function v73_scalar: CFLAGS += -Wno-unused-function +fp_hvx: fp_hvx.c hvx_misc.h hex_test.h +fp_hvx: CFLAGS += -mhvx -mhvx-ieee-fp +fp_hvx_disabled: fp_hvx_disabled.c hvx_misc.h hex_test.h +fp_hvx_disabled: CFLAGS += -mhvx -mhvx-ieee-fp + +run-fp_hvx_disabled: QEMU_OPTS += -cpu v73,ieee-fp=false hvx_histogram: hvx_histogram.c hvx_histogram_row.S $(CC) $(CFLAGS) $(CROSS_CC_GUEST_CFLAGS) $^ -o $@ $(LDFLAGS) diff --git a/tests/tcg/hexagon/fp_hvx.c b/tests/tcg/hexagon/fp_hvx.c new file mode 100644 index 0000000000..a309654ed7 --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +int err; +#include "hvx_misc.h" + +#if __HEXAGON_ARCH__ > 75 +#error "After v75, compiler will replace some FP HVX instructions." +#endif + +/****************************************************************************** + * NAN handling + *****************************************************************************/ + +#define isnan(X) \ + (sizeof(X) == bytes_hf ? ((raw_hf(X) & ~0x8000) > 0x7c00) : \ + ((raw_sf(X) & ~(1 << 31)) > 0x7f800000UL)) + +#define CHECK_NAN(A, DEF_NAN) (isnan(A) ? DEF_NAN : (A)) +#define NAN_SF float_sf(0x7FFFFFFF) +#define NAN_HF float_hf(0x7FFF) + +/****************************************************************************** + * Binary operations + *****************************************************************************/ + +#define DEF_TEST_OP_2(vop, op, type_res, type_arg) \ + static void test_##vop##_##type_res##_##type_arg(void) \ + { \ + memset(expect, 0xff, sizeof(expect)); \ + memset(output, 0xff, sizeof(output)); \ + for (int i = 0; i < BUFSIZE; i++) { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[i]; \ + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; \ + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; \ + *hvx_output = \ + Q6_V##type_res##_##vop##_V##type_arg##V##type_arg(hvx_buffer0, \ + hvx_buffer1); \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / bytes_##type_res; j++) { \ + expect[i].type_res[j] = \ + raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[j]), \ + float_##type_arg(buffer1[i].type_arg[j]))); \ + } \ + } \ + check_output_##type_res(__LINE__, BUFSIZE); \ + } + +#define SUM(X, Y, DEF_NAN) CHECK_NAN((X) + (Y), DEF_NAN) +#define SUB(X, Y, DEF_NAN) CHECK_NAN((X) - (Y), DEF_NAN) +#define MULT(X, Y, DEF_NAN) CHECK_NAN((X) * (Y), DEF_NAN) + +#define SUM_SF(X, Y) SUM(X, Y, NAN_SF) +#define SUM_HF(X, Y) SUM(X, Y, NAN_HF) +#define SUB_SF(X, Y) SUB(X, Y, NAN_SF) +#define SUB_HF(X, Y) SUB(X, Y, NAN_HF) +#define MULT_SF(X, Y) MULT(X, Y, NAN_SF) +#define MULT_HF(X, Y) MULT(X, Y, NAN_HF) + +DEF_TEST_OP_2(vadd, SUM_SF, sf, sf); +DEF_TEST_OP_2(vadd, SUM_HF, hf, hf); +DEF_TEST_OP_2(vsub, SUB_SF, sf, sf); +DEF_TEST_OP_2(vsub, SUB_HF, hf, hf); +DEF_TEST_OP_2(vmpy, MULT_SF, sf, sf); +DEF_TEST_OP_2(vmpy, MULT_HF, hf, hf); + +/****************************************************************************** + * Other tests + *****************************************************************************/ + +static void test_vdmpy_sf_hf(bool acc) +{ + memset(expect, 0xff, sizeof(expect)); + + for (int i = 0; i < BUFSIZE; i++) { + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; + HVX_Vector *hvx_output = (HVX_Vector *)&output[i]; + + uint32_t PREFIL_VAL = 0x111222; + *hvx_output = Q6_V_vsplat_R(PREFIL_VAL); + + if (!acc) { + *hvx_output = Q6_Vsf_vdmpy_VhfVhf(hvx_buffer0, hvx_buffer1); + } else { + *hvx_output = Q6_Vsf_vdmpyacc_VsfVhfVhf(*hvx_output, hvx_buffer0, + hvx_buffer1); + } + + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + float a1 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j + 1])); + float a2 = float_hf_to_sf(float_hf(buffer0[i].hf[2 * j])); + float a3 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j + 1])); + float a4 = float_hf_to_sf(float_hf(buffer1[i].hf[2 * j])); + /* + * Note, IEEE FP specifies +0.0 + -0.0 == +0.0. So we use -0.0 in + * the default case to preserve the zero sign. + */ + float prev = acc ? float_sf(PREFIL_VAL) : -0.0; + expect[i].sf[j] = raw_sf(CHECK_NAN((a1 * a3) + (a2 * a4) + prev, NAN_SF)); + } + } + check_output_sf(__LINE__, BUFSIZE); +} + +static void test_new(void) +{ + asm volatile("r0 = #%2\n" + "v0 = vsplat(r0)\n" + "vmem(%1 + #0) = v0\n" + "r1 = #%3\n" + "v1 = vsplat(r1)\n" + "v2 = vsplat(r1)\n" + "{\n" + " v0.sf = vadd(v1.sf, v2.sf)\n" + " vmem(%0 + #0) = v0.new\n" + "}\n" + : + : "r"(output), "r"(expect), "i"(SF_two), "i"(SF_one) + : "r0", "r1", "v0", "v1", "v2", "memory"); + check_output_w(__LINE__, 1); +} + +int main(void) +{ + init_buffers_fp(); + + /* add/sub */ + test_vadd_sf_sf(); + test_vadd_hf_hf(); + test_vsub_sf_sf(); + test_vsub_hf_hf(); + + /* multiply */ + test_vmpy_sf_sf(); + test_vmpy_hf_hf(); + + /* dot product */ + test_vdmpy_sf_hf(false); + test_vdmpy_sf_hf(true); + + test_new(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/fp_hvx_disabled.c b/tests/tcg/hexagon/fp_hvx_disabled.c new file mode 100644 index 0000000000..388a42e2b7 --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx_disabled.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include + +int err; +#include "hvx_misc.h" + +static void test_disabled(void) +{ + memset(output, 0xAA, sizeof(output)); + memset(expect, 0, sizeof(expect)); + asm volatile("r0 = #0xff\n" + "v0 = vsplat(r0)\n" + "r1 = #0x1\n" + "v1 = vsplat(r1)\n" + "v2 = vsplat(r1)\n" + "v0.sf = vadd(v1.sf, v2.sf)\n" + "vmem(%0 + #0) = v0\n" + : + : "r"(output) + : "r0", "r1", "v0", "v1", "v2", "memory"); + check_output_w(__LINE__, 1); +} + +static void test_disabled_with_new(void) +{ + memset(output, 0xAA, sizeof(output)); + memset(expect, 0, sizeof(expect)); + asm volatile("r0 = #0xff\n" + "v0 = vsplat(r0)\n" + "r1 = #0x1\n" + "v1 = vsplat(r1)\n" + "v2 = vsplat(r1)\n" + "{\n" + " v0.sf = vadd(v1.sf, v2.sf)\n" + " vmem(%0 + #0) = v0.new\n" + "}\n" + : + : "r"(output) + : "r0", "r1", "v0", "v1", "v2", "memory"); + check_output_w(__LINE__, 1); +} + +int main(void) +{ + test_disabled(); + test_disabled_with_new(); + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/hex_test.h b/tests/tcg/hexagon/hex_test.h index cfed06a58b..e7a6644d41 100644 --- a/tests/tcg/hexagon/hex_test.h +++ b/tests/tcg/hexagon/hex_test.h @@ -19,6 +19,8 @@ #ifndef HEX_TEST_H #define HEX_TEST_H +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + static inline void __check32(int line, uint32_t val, uint32_t expect) { if (val != expect) { @@ -110,6 +112,7 @@ static inline void __check64_ne(int line, uint64_t val, uint64_t expect) /* Some useful floating point values */ const uint32_t SF_INF = 0x7f800000; +const uint32_t SF_INF_neg = 0xff800000; const uint32_t SF_QNaN = 0x7fc00000; const uint32_t SF_QNaN_special = 0x7f800001; const uint32_t SF_SNaN = 0x7fb00000; @@ -128,6 +131,7 @@ const uint32_t SF_large_pos = 0x5afa572e; const uint32_t SF_any = 0x3f800000; const uint32_t SF_denorm = 0x00000001; const uint32_t SF_random = 0x346001d6; +const uint32_t SF_neg_two = 0xc0000000; const uint64_t DF_QNaN = 0x7ff8000000000000ULL; const uint64_t DF_SNaN = 0x7ff7000000000000ULL; diff --git a/tests/tcg/hexagon/hvx_misc.h b/tests/tcg/hexagon/hvx_misc.h index 2e868340fd..0330cb289d 100644 --- a/tests/tcg/hexagon/hvx_misc.h +++ b/tests/tcg/hexagon/hvx_misc.h @@ -18,6 +18,8 @@ #ifndef HVX_MISC_H #define HVX_MISC_H +#include "hex_test.h" + static inline void check(int line, int i, int j, uint64_t result, uint64_t expect) { @@ -34,8 +36,10 @@ typedef union { uint64_t ud[MAX_VEC_SIZE_BYTES / 8]; int64_t d[MAX_VEC_SIZE_BYTES / 8]; uint32_t uw[MAX_VEC_SIZE_BYTES / 4]; + uint32_t sf[MAX_VEC_SIZE_BYTES / 4]; /* convenience alias */ int32_t w[MAX_VEC_SIZE_BYTES / 4]; uint16_t uh[MAX_VEC_SIZE_BYTES / 2]; + uint16_t hf[MAX_VEC_SIZE_BYTES / 2]; /* convenience alias */ int16_t h[MAX_VEC_SIZE_BYTES / 2]; uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; int8_t b[MAX_VEC_SIZE_BYTES / 1]; @@ -63,7 +67,9 @@ static inline void check_output_##FIELD(int line, size_t num_vectors) \ CHECK_OUTPUT_FUNC(d, 8) CHECK_OUTPUT_FUNC(w, 4) +CHECK_OUTPUT_FUNC(sf, 4) CHECK_OUTPUT_FUNC(h, 2) +CHECK_OUTPUT_FUNC(hf, 2) CHECK_OUTPUT_FUNC(b, 1) static inline void init_buffers(void) @@ -81,6 +87,33 @@ static inline void init_buffers(void) } } +static const uint32_t FP_VALUES[] = { + SF_INF, SF_INF_neg, SF_QNaN, SF_QNaN_special, SF_SNaN, SF_QNaN_neg, + SF_SNaN_neg, SF_HEX_NaN, SF_zero, SF_zero_neg, SF_one, SF_one_recip, + SF_one_invsqrta, SF_two, SF_four, SF_small_neg, SF_large_pos, SF_any, + SF_denorm, SF_random, SF_neg_two, +}; +#define FP_VALUES_MAX ARRAY_SIZE(FP_VALUES) + +static inline void init_buffers_fp(void) +{ + _Static_assert(BUFSIZE * (MAX_VEC_SIZE_BYTES / 4) > + FP_VALUES_MAX * FP_VALUES_MAX, + "test arrays can't fit all FP_VALUES combinations"); + int counter1 = 0, counter2 = 0; + for (int i = 0; i < BUFSIZE; i++) { + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 4; j++) { + buffer0[i].sf[j] = FP_VALUES[counter1]; + buffer1[i].sf[j] = FP_VALUES[counter2]; + counter2++; + if (counter2 == FP_VALUES_MAX) { + counter2 = 0; + counter1 = (counter1 + 1) % FP_VALUES_MAX; + } + } + } +} + #define VEC_OP1(ASM, EL, IN, OUT) \ asm("v2 = vmem(%0 + #0)\n\t" \ "v2" #EL " = " #ASM "(v2" #EL ")\n\t" \ @@ -175,4 +208,12 @@ static inline void test_##NAME(bool invert) \ check_output_b(__LINE__, BUFSIZE); \ } +#define float_sf(x) ({ typeof(x) _x = (x); *((float *)&(_x)); }) +#define float_hf(x) ({ typeof(x) _x = (x); *((_Float16 *) &(_x)); }) +#define raw_sf(x) ({ typeof(x) _x = (x); *((uint32_t *)&(_x)); }) +#define raw_hf(x) ({ typeof(x) _x = (x); *((uint16_t *)&(_x)); }) +#define float_hf_to_sf(x) ((float)x) +#define bytes_hf 2 +#define bytes_sf 4 + #endif From 7f3a641101f683549e60de55d30c5006ee97d996 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:39:02 -0700 Subject: [PATCH 433/860] tests/hexagon: add tests for v68 HVX IEEE float min/max Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/7fdbc12ed4c62a16068c380a85ee2356051e61ea.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/tcg/hexagon/fp_hvx.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/tcg/hexagon/fp_hvx.c b/tests/tcg/hexagon/fp_hvx.c index a309654ed7..dee0781bd7 100644 --- a/tests/tcg/hexagon/fp_hvx.c +++ b/tests/tcg/hexagon/fp_hvx.c @@ -73,6 +73,30 @@ DEF_TEST_OP_2(vsub, SUB_HF, hf, hf); DEF_TEST_OP_2(vmpy, MULT_SF, sf, sf); DEF_TEST_OP_2(vmpy, MULT_HF, hf, hf); +#define signbit_fp(X) \ + (sizeof(X) == bytes_hf ? ((raw_hf(X) & 0x8000) != 0) : \ + ((raw_sf(X) & 0x80000000) != 0)) + +#define STD_MIN(X, Y) ((X) < (Y) ? (X) : (Y)) +#define STD_MAX(X, Y) ((X) > (Y) ? (X) : (Y)) + +#define MIN(X, Y, DEF_NAN) \ + ((isnan(X) || isnan(Y)) ? DEF_NAN : \ + ((X) != (Y)) ? STD_MIN(X, Y) : (signbit_fp(X) ? (X) : (Y))) /* -0 < +0 */ +#define MAX(X, Y, DEF_NAN) \ + ((isnan(X) || isnan(Y)) ? DEF_NAN : \ + ((X) != (Y)) ? STD_MAX(X, Y) : (signbit_fp(X) ? (Y) : (X))) /* -0 < +0 */ + +#define MIN_HF(X, Y) MIN(X, Y, NAN_HF) +#define MAX_HF(X, Y) MAX(X, Y, NAN_HF) +#define MIN_SF(X, Y) MIN(X, Y, NAN_SF) +#define MAX_SF(X, Y) MAX(X, Y, NAN_SF) + +DEF_TEST_OP_2(vfmin, MIN_SF, sf, sf); +DEF_TEST_OP_2(vfmax, MAX_SF, sf, sf); +DEF_TEST_OP_2(vfmin, MIN_HF, hf, hf); +DEF_TEST_OP_2(vfmax, MAX_HF, hf, hf); + /****************************************************************************** * Other tests *****************************************************************************/ @@ -150,6 +174,12 @@ int main(void) test_new(); + /* min/max */ + test_vfmin_sf_sf(); + test_vfmin_hf_hf(); + test_vfmax_sf_sf(); + test_vfmax_hf_hf(); + puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; } From 57da1eb994e4fe7073e9079864ba6d94a7a55ee7 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:39:03 -0700 Subject: [PATCH 434/860] tests/hexagon: add tests for v68 HVX IEEE float conversions Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/ec6af07a285c47e5f6df343860bfedce5bed9421.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/tcg/hexagon/Makefile.target | 3 + tests/tcg/hexagon/fp_hvx_cvt.c | 188 ++++++++++++++++++++++++++++++ tests/tcg/hexagon/hex_test.h | 14 +++ tests/tcg/hexagon/hvx_misc.h | 2 + 4 files changed, 207 insertions(+) create mode 100644 tests/tcg/hexagon/fp_hvx_cvt.c diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 2a5958711b..0978e7ca91 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -51,6 +51,7 @@ HEX_TESTS += scatter_gather HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram HEX_TESTS += fp_hvx +HEX_TESTS += fp_hvx_cvt HEX_TESTS += fp_hvx_disabled HEX_TESTS += invalid-slots HEX_TESTS += valid-slots @@ -142,6 +143,8 @@ fp_hvx: fp_hvx.c hvx_misc.h hex_test.h fp_hvx: CFLAGS += -mhvx -mhvx-ieee-fp fp_hvx_disabled: fp_hvx_disabled.c hvx_misc.h hex_test.h fp_hvx_disabled: CFLAGS += -mhvx -mhvx-ieee-fp +fp_hvx_cvt: fp_hvx_cvt.c hvx_misc.h hex_test.h +fp_hvx_cvt: CFLAGS += -mhvx -mhvx-ieee-fp run-fp_hvx_disabled: QEMU_OPTS += -cpu v73,ieee-fp=false diff --git a/tests/tcg/hexagon/fp_hvx_cvt.c b/tests/tcg/hexagon/fp_hvx_cvt.c new file mode 100644 index 0000000000..b08eba92da --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx_cvt.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include + +#if __HEXAGON_ARCH__ > 75 +#error "After v75, compiler will replace some FP HVX instructions." +#endif + +int err; +#include "hvx_misc.h" +#include "hex_test.h" + +#define TEST_EXP(TO, FROM, VAL, EXP) do { \ + ((MMVector *)&buffer)->FROM[index] = VAL; \ + expect[0].TO[index] = EXP; \ + index++; \ +} while (0) + +#define DEF_TEST_CVT(TO, FROM, TESTS) \ + static void test_vcvt_##TO##_##FROM(void) \ + { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; \ + HVX_Vector buffer; \ + int index = 0; \ + memset(&buffer, 0, sizeof(buffer)); \ + memset(expect, 0, sizeof(expect)); \ + TESTS \ + * hvx_output = Q6_V##TO##_vcvt_V##FROM(buffer); \ + check_output_##TO(__LINE__, 1); \ + } + +DEF_TEST_CVT(uh, hf, { \ + TEST_EXP(uh, hf, HF_QNaN, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_SNaN, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_QNaN_neg, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_INF, UINT16_MAX); \ + TEST_EXP(uh, hf, HF_INF_neg, 0); \ + TEST_EXP(uh, hf, HF_neg_two, 0); \ + TEST_EXP(uh, hf, HF_zero_neg, 0); \ + TEST_EXP(uh, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP(uh, hf, HF_one_recip, 1); \ +}) + +DEF_TEST_CVT(h, hf, { \ + TEST_EXP(h, hf, HF_QNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_SNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_QNaN_neg, INT16_MAX); \ + TEST_EXP(h, hf, HF_INF, INT16_MAX); \ + TEST_EXP(h, hf, HF_INF_neg, INT16_MIN); \ + TEST_EXP(h, hf, HF_neg_two, -2); \ + TEST_EXP(h, hf, HF_zero_neg, 0); \ + TEST_EXP(h, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP(h, hf, HF_one_recip, 1); \ +}) + +/* + * Some cvt operations take two vectors as input and perform the following: + * VdV.TO[4*i] = OP(VuV.FROM[2*i]); + * VdV.TO[4*i+1] = OP(VuV.FROM[2*i+1]); + * VdV.TO[4*i+2] = OP(VvV.FROM[2*i]); + * VdV.TO[4*i+3] = OP(VvV.FROM[2*i+1])) + * We use bf_index and index in a way that the tests are always done either + * using the first or third line of the above snippet. + */ +#define TEST_EXP_2(TO, FROM, VAL, EXP) do { \ + ((MMVector *)&buffers[bf_index])->FROM[2 * index] = VAL; \ + expect[0].TO[(4 * index) + (2 * bf_index)] = EXP; \ + index++; \ + bf_index = (bf_index + 1) % 2; \ +} while (0) + +#define DEF_TEST_CVT_2(TO, FROM, TESTS) \ + static void test_vcvt_##TO##_##FROM(void) \ + { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; \ + HVX_Vector buffers[2]; \ + int index = 0, bf_index = 0; \ + memset(&buffers, 0, sizeof(buffers)); \ + memset(expect, 0, sizeof(expect)); \ + TESTS \ + * hvx_output = Q6_V##TO##_vcvt_V##FROM##V##FROM(buffers[0], buffers[1]); \ + check_output_##TO(__LINE__, 1); \ + } + +DEF_TEST_CVT_2(ub, hf, { \ + TEST_EXP_2(ub, hf, HF_QNaN, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_SNaN, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_QNaN_neg, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_INF, UINT8_MAX); \ + TEST_EXP_2(ub, hf, HF_INF_neg, 0); \ + TEST_EXP_2(ub, hf, HF_small_neg, 0); \ + TEST_EXP_2(ub, hf, HF_neg_two, 0); \ + TEST_EXP_2(ub, hf, HF_zero_neg, 0); \ + TEST_EXP_2(ub, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP_2(ub, hf, HF_one_recip, 1); \ +}) + +DEF_TEST_CVT_2(b, hf, { \ + TEST_EXP_2(b, hf, HF_QNaN, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_SNaN, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_QNaN_neg, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_INF, INT8_MAX); \ + TEST_EXP_2(b, hf, HF_INF_neg, INT8_MIN); \ + TEST_EXP_2(b, hf, HF_small_neg, 0); \ + TEST_EXP_2(b, hf, HF_neg_two, -2); \ + TEST_EXP_2(b, hf, HF_zero_neg, 0); \ + TEST_EXP_2(b, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP_2(b, hf, HF_one_recip, 1); \ +}) + +#define DEF_TEST_VCONV(TO, FROM, TESTS) \ + static void test_vconv_##TO##_##FROM(void) \ + { \ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; \ + HVX_Vector buffer; \ + int index = 0; \ + memset(&buffer, 0, sizeof(buffer)); \ + memset(expect, 0, sizeof(expect)); \ + TESTS \ + * hvx_output = Q6_V##TO##_equals_V##FROM(buffer); \ + check_output_##TO(__LINE__, 1); \ + } + +DEF_TEST_VCONV(w, sf, { \ + TEST_EXP(w, sf, SF_QNaN, INT32_MAX); \ + TEST_EXP(w, sf, SF_SNaN, INT32_MAX); \ + TEST_EXP(w, sf, SF_QNaN_neg, INT32_MIN); \ + TEST_EXP(w, sf, SF_INF, INT32_MAX); \ + TEST_EXP(w, sf, SF_INF_neg, INT32_MIN); \ + TEST_EXP(w, sf, SF_small_neg, 0); \ + TEST_EXP(w, sf, SF_neg_two, -2); \ + TEST_EXP(w, sf, SF_zero_neg, 0); \ + TEST_EXP(w, sf, raw_sf(2.1f), 2); \ + TEST_EXP(w, sf, raw_sf(2.8f), 2); \ +}) + +DEF_TEST_VCONV(h, hf, { \ + TEST_EXP(h, hf, HF_QNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_SNaN, INT16_MAX); \ + TEST_EXP(h, hf, HF_QNaN_neg, INT16_MIN); \ + TEST_EXP(h, hf, HF_INF, INT16_MAX); \ + TEST_EXP(h, hf, HF_INF_neg, INT16_MIN); \ + TEST_EXP(h, hf, HF_small_neg, 0); \ + TEST_EXP(h, hf, HF_neg_two, -2); \ + TEST_EXP(h, hf, HF_zero_neg, 0); \ + TEST_EXP(h, hf, raw_hf((_Float16)2.1), 2); \ + TEST_EXP(h, hf, raw_hf((_Float16)2.8), 2); \ +}) + +DEF_TEST_VCONV(hf, h, { \ + TEST_EXP(hf, h, 0, HF_zero); \ + TEST_EXP(hf, h, 2, HF_two); \ + TEST_EXP(hf, h, -2, HF_neg_two); \ + TEST_EXP(hf, h, 2049, raw_hf((_Float16)2048)); /* rounds DOWN */ \ + TEST_EXP(hf, h, 2051, raw_hf((_Float16)2052)); /* rounds UP */ \ +}) + +DEF_TEST_VCONV(sf, w, { \ + TEST_EXP(sf, w, 0, SF_zero); \ + TEST_EXP(sf, w, 2, SF_two); \ + TEST_EXP(sf, w, -2, SF_neg_two); \ + TEST_EXP(sf, w, 16777217, raw_sf((float)16777216)); /* rounds DOWN */ \ + TEST_EXP(sf, w, 16777219, raw_sf((float)16777220)); /* rounds UP */ \ +}) + +int main(void) +{ + test_vcvt_uh_hf(); + test_vcvt_h_hf(); + test_vcvt_ub_hf(); + test_vcvt_b_hf(); + test_vconv_w_sf(); + test_vconv_sf_w(); + test_vconv_h_hf(); + test_vconv_hf_h(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/hex_test.h b/tests/tcg/hexagon/hex_test.h index e7a6644d41..d5da8ad240 100644 --- a/tests/tcg/hexagon/hex_test.h +++ b/tests/tcg/hexagon/hex_test.h @@ -111,6 +111,20 @@ static inline void __check64_ne(int line, uint64_t val, uint64_t expect) "usr = r2\n\t" /* Some useful floating point values */ +const uint16_t HF_INF = 0x7c00; +const uint16_t HF_INF_neg = 0xfc00; +const uint16_t HF_QNaN = 0x7e00; +const uint16_t HF_SNaN = 0x7d00; +const uint16_t HF_QNaN_neg = 0xfe00; +const uint16_t HF_zero = 0x0000; +const uint16_t HF_zero_neg = 0x8000; +const uint16_t HF_one = 0x3c00; +const uint16_t HF_one_recip = 0x3bf9; +const uint16_t HF_two = 0x4000; +const uint16_t HF_small_neg = 0x8010; +const uint16_t HF_any = 0x3c00; +const uint16_t HF_neg_two = 0xc000; + const uint32_t SF_INF = 0x7f800000; const uint32_t SF_INF_neg = 0xff800000; const uint32_t SF_QNaN = 0x7fc00000; diff --git a/tests/tcg/hexagon/hvx_misc.h b/tests/tcg/hexagon/hvx_misc.h index 0330cb289d..43de20da6a 100644 --- a/tests/tcg/hexagon/hvx_misc.h +++ b/tests/tcg/hexagon/hvx_misc.h @@ -69,7 +69,9 @@ CHECK_OUTPUT_FUNC(d, 8) CHECK_OUTPUT_FUNC(w, 4) CHECK_OUTPUT_FUNC(sf, 4) CHECK_OUTPUT_FUNC(h, 2) +CHECK_OUTPUT_FUNC(uh, 2) CHECK_OUTPUT_FUNC(hf, 2) +CHECK_OUTPUT_FUNC(ub, 1) CHECK_OUTPUT_FUNC(b, 1) static inline void init_buffers(void) From 6bebed5a4771eb8149a9560c762e7dd326f35429 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:39:04 -0700 Subject: [PATCH 435/860] tests/hexagon: add tests for v68 HVX IEEE float comparisons Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/53e73707092d31bcde6ed8189c5496b00345f8fa.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/tcg/hexagon/Makefile.target | 3 + tests/tcg/hexagon/fp_hvx_cmp.c | 224 ++++++++++++++++++++++++++++++ tests/tcg/hexagon/hex_test.h | 1 + 3 files changed, 228 insertions(+) create mode 100644 tests/tcg/hexagon/fp_hvx_cmp.c diff --git a/tests/tcg/hexagon/Makefile.target b/tests/tcg/hexagon/Makefile.target index 0978e7ca91..61adf6356e 100644 --- a/tests/tcg/hexagon/Makefile.target +++ b/tests/tcg/hexagon/Makefile.target @@ -52,6 +52,7 @@ HEX_TESTS += hvx_misc HEX_TESTS += hvx_histogram HEX_TESTS += fp_hvx HEX_TESTS += fp_hvx_cvt +HEX_TESTS += fp_hvx_cmp HEX_TESTS += fp_hvx_disabled HEX_TESTS += invalid-slots HEX_TESTS += valid-slots @@ -145,6 +146,8 @@ fp_hvx_disabled: fp_hvx_disabled.c hvx_misc.h hex_test.h fp_hvx_disabled: CFLAGS += -mhvx -mhvx-ieee-fp fp_hvx_cvt: fp_hvx_cvt.c hvx_misc.h hex_test.h fp_hvx_cvt: CFLAGS += -mhvx -mhvx-ieee-fp +fp_hvx_cmp: fp_hvx_cmp.c hvx_misc.h hex_test.h +fp_hvx_cmp: CFLAGS += -mhvx -mhvx-ieee-fp run-fp_hvx_disabled: QEMU_OPTS += -cpu v73,ieee-fp=false diff --git a/tests/tcg/hexagon/fp_hvx_cmp.c b/tests/tcg/hexagon/fp_hvx_cmp.c new file mode 100644 index 0000000000..8068d1c3ee --- /dev/null +++ b/tests/tcg/hexagon/fp_hvx_cmp.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +#if __HEXAGON_ARCH__ > 75 +#error "After v75, compiler will replace some FP HVX instructions." +#endif + +int err; +#include "hvx_misc.h" +#include "hex_test.h" + +#define MAX_TESTS_hf (MAX_VEC_SIZE_BYTES / 2) +#define MAX_TESTS_sf (MAX_VEC_SIZE_BYTES / 4) + +#define TRUE_MASK_sf 0xffffffff +#define TRUE_MASK_hf 0xffff + +static const char *comparisons[MAX_TESTS_sf][2]; +static HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; +static HVX_Vector buffers[2], true_vec, false_vec; +static int exp_index; + +#define ADD_TEST_CMP(TYPE, VAL1, VAL2, EXP) do { \ + ((MMVector *)&buffers[0])->TYPE[exp_index] = VAL1; \ + ((MMVector *)&buffers[1])->TYPE[exp_index] = VAL2; \ + expect[0].TYPE[exp_index] = EXP ? TRUE_MASK_##TYPE : 0; \ + comparisons[exp_index][0] = #VAL1; \ + comparisons[exp_index][1] = #VAL2; \ + assert(exp_index < MAX_TESTS_##TYPE); \ + exp_index++; \ +} while (0) + +#define TEST_CMP_GT(TYPE, VAL1, VAL2) do { \ + ADD_TEST_CMP(TYPE, VAL1, VAL2, true); \ + ADD_TEST_CMP(TYPE, VAL2, VAL1, false); \ +} while (0) + +#define PREP_TEST() do { \ + memset(&buffers, 0, sizeof(buffers)); \ + memset(expect, 0, sizeof(expect)); \ + exp_index = 0; \ +} while (0) + +#define CHECK(TYPE, TYPESZ) do { \ + HVX_VectorPred pred = Q6_Q_vcmp_gt_V##TYPE##V##TYPE(buffers[0], buffers[1]); \ + *hvx_output = Q6_V_vmux_QVV(pred, true_vec, false_vec); \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / TYPESZ; j++) { \ + if (output[0].TYPE[j] != expect[0].TYPE[j]) { \ + printf("ERROR: expected %s %s %s\n", comparisons[j][0], \ + (expect[0].TYPE[j] != 0 ? ">" : "<="), comparisons[j][1]); \ + err++; \ + } \ + } \ +} while (0) + +static void test_cmp_sf(void) +{ + /* + * General ordering for sf: + * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg + */ + + /* Test equality */ + PREP_TEST(); + ADD_TEST_CMP(sf, raw_sf(2.2), raw_sf(2.2), false); + ADD_TEST_CMP(sf, SF_SNaN, SF_SNaN, false); + CHECK(sf, 4); + + /* Common numbers */ + PREP_TEST(); + TEST_CMP_GT(sf, raw_sf(2.2), raw_sf(2.1)); + TEST_CMP_GT(sf, raw_sf(0), raw_sf(-2.2)); + CHECK(sf, 4); + + /* Infinity vs Infinity/NaN */ + PREP_TEST(); + TEST_CMP_GT(sf, SF_QNaN, SF_INF); + TEST_CMP_GT(sf, SF_SNaN, SF_INF); + TEST_CMP_GT(sf, SF_INF, SF_INF_neg); + TEST_CMP_GT(sf, SF_INF, SF_SNaN_neg); + TEST_CMP_GT(sf, SF_INF, SF_QNaN_neg); + TEST_CMP_GT(sf, SF_INF_neg, SF_SNaN_neg); + TEST_CMP_GT(sf, SF_INF_neg, SF_QNaN_neg); + TEST_CMP_GT(sf, SF_SNaN, SF_INF_neg); + TEST_CMP_GT(sf, SF_QNaN, SF_INF_neg); + CHECK(sf, 4); + + /* NaN vs NaN */ + PREP_TEST(); + TEST_CMP_GT(sf, SF_QNaN, SF_SNaN); + TEST_CMP_GT(sf, SF_SNaN, SF_SNaN_neg); + TEST_CMP_GT(sf, SF_SNaN_neg, SF_QNaN_neg); + CHECK(sf, 4); + + /* NaN vs non-NaN */ + PREP_TEST(); + TEST_CMP_GT(sf, SF_QNaN, SF_one); + TEST_CMP_GT(sf, SF_SNaN, SF_one); + TEST_CMP_GT(sf, SF_one, SF_QNaN_neg); + TEST_CMP_GT(sf, SF_one, SF_SNaN_neg); + CHECK(sf, 4); +} + +static void test_cmp_hf(void) +{ + /* + * General ordering for hf: + * QNaN > SNaN > +Inf > numbers > -Inf > QSNaN_neg > QNaN_neg + */ + + /* Test equality */ + PREP_TEST(); + ADD_TEST_CMP(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.2), false); + ADD_TEST_CMP(hf, HF_SNaN, HF_SNaN, false); + CHECK(hf, 2); + + /* Common numbers */ + PREP_TEST(); + TEST_CMP_GT(hf, raw_hf((_Float16)2.2), raw_hf((_Float16)2.1)); + TEST_CMP_GT(hf, raw_hf((_Float16)0), raw_hf((_Float16) - 2.2)); + CHECK(hf, 2); + + /* Infinity vs Infinity/NaN */ + PREP_TEST(); + TEST_CMP_GT(hf, HF_QNaN, HF_INF); + TEST_CMP_GT(hf, HF_SNaN, HF_INF); + TEST_CMP_GT(hf, HF_INF, HF_INF_neg); + TEST_CMP_GT(hf, HF_INF, HF_SNaN_neg); + TEST_CMP_GT(hf, HF_INF, HF_QNaN_neg); + TEST_CMP_GT(hf, HF_INF_neg, HF_SNaN_neg); + TEST_CMP_GT(hf, HF_INF_neg, HF_QNaN_neg); + TEST_CMP_GT(hf, HF_SNaN, HF_INF_neg); + TEST_CMP_GT(hf, HF_QNaN, HF_INF_neg); + CHECK(hf, 2); + + /* NaN vs NaN */ + PREP_TEST(); + TEST_CMP_GT(hf, HF_QNaN, HF_SNaN); + TEST_CMP_GT(hf, HF_SNaN, HF_SNaN_neg); + TEST_CMP_GT(hf, HF_SNaN_neg, HF_QNaN_neg); + CHECK(hf, 2); + + /* NaN vs non-NaN */ + PREP_TEST(); + TEST_CMP_GT(hf, HF_QNaN, HF_one); + TEST_CMP_GT(hf, HF_SNaN, HF_one); + TEST_CMP_GT(hf, HF_one, HF_QNaN_neg); + TEST_CMP_GT(hf, HF_one, HF_SNaN_neg); + CHECK(hf, 2); +} + +static void check_byte_pred(HVX_VectorPred pred, int byte_idx, uint8_t exp_mask, + int line) +{ + /* + * Note: ((uint8_t *)&pred)[N] returns the expanded value of bit N: + * 0xFF if bit is set, 0x00 if clear. + */ + for (int i = 0; i < 8; i++) { + int idx = byte_idx * 8 + i; + int val = ((uint8_t *)&pred)[idx]; + int exp = (exp_mask >> i) & 1 ? 0xff : 0x00; + if (exp != val) { + printf("ERROR line %d: pred bit %d is 0x%x, should be 0x%x\n", + line, idx, val, exp); + err++; + } + } +} + +#define CHECK_BYTE_PRED(PRED, BYTE, EXP) check_byte_pred(PRED, BYTE, EXP, __LINE__) + +static void test_cmp_variants(void) +{ + HVX_VectorPred pred; + + /* + * Setup: comparison result will have bits 4-7 set (0xF0 in pred byte 0) + * - sf[0]: SF_zero > SF_one = false -> bits 0-3 = 0 + * - sf[1]: SF_one > SF_zero = true -> bits 4-7 = 1 + */ + PREP_TEST(); + ADD_TEST_CMP(sf, SF_zero, SF_one, false); + ADD_TEST_CMP(sf, SF_one, SF_zero, true); + + /* greater and: 0xF0 & 0xF0 = 0xF0 */ + memset(&pred, 0xF0, sizeof(pred)); + pred = Q6_Q_vcmp_gtand_QVsfVsf(pred, buffers[0], buffers[1]); + CHECK_BYTE_PRED(pred, 0, 0xF0); + + /* greater or: 0x0F | 0xF0 = 0xFF */ + memset(&pred, 0x0F, sizeof(pred)); + pred = Q6_Q_vcmp_gtor_QVsfVsf(pred, buffers[0], buffers[1]); + CHECK_BYTE_PRED(pred, 0, 0xFF); + + /* greater xor: 0xFF ^ 0xF0 = 0x0F */ + memset(&pred, 0xFF, sizeof(pred)); + pred = Q6_Q_vcmp_gtxacc_QVsfVsf(pred, buffers[0], buffers[1]); + CHECK_BYTE_PRED(pred, 0, 0x0F); +} + +int main(void) +{ + memset(&true_vec, 0xff, sizeof(true_vec)); + memset(&false_vec, 0, sizeof(false_vec)); + + test_cmp_sf(); + test_cmp_hf(); + test_cmp_variants(); + + puts(err ? "FAIL" : "PASS"); + return err ? 1 : 0; +} diff --git a/tests/tcg/hexagon/hex_test.h b/tests/tcg/hexagon/hex_test.h index d5da8ad240..79d30ec61c 100644 --- a/tests/tcg/hexagon/hex_test.h +++ b/tests/tcg/hexagon/hex_test.h @@ -115,6 +115,7 @@ const uint16_t HF_INF = 0x7c00; const uint16_t HF_INF_neg = 0xfc00; const uint16_t HF_QNaN = 0x7e00; const uint16_t HF_SNaN = 0x7d00; +const uint16_t HF_SNaN_neg = 0xfd00; const uint16_t HF_QNaN_neg = 0xfe00; const uint16_t HF_zero = 0x0000; const uint16_t HF_zero_neg = 0x8000; From 2e555f5b44d1586517e44d47f73afa6062f48b93 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Thu, 16 Apr 2026 04:39:05 -0700 Subject: [PATCH 436/860] tests/hexagon: add tests for HVX bfloat Reviewed-by: Taylor Simpson Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Brian Cain Link: https://lore.kernel.org/qemu-devel/392114c4c16e6f7f2835a6513987aafea82b565c.1776339451.git.matheus.bernardino@oss.qualcomm.com Signed-off-by: Brian Cain --- tests/tcg/hexagon/fp_hvx.c | 41 +++++++++++++++++++++++++++ tests/tcg/hexagon/fp_hvx_cmp.c | 51 ++++++++++++++++++++++++++++++++++ tests/tcg/hexagon/fp_hvx_cvt.c | 31 +++++++++++++++++++++ tests/tcg/hexagon/hex_test.h | 13 +++++++++ tests/tcg/hexagon/hvx_misc.h | 30 ++++++++++++++++++++ 5 files changed, 166 insertions(+) diff --git a/tests/tcg/hexagon/fp_hvx.c b/tests/tcg/hexagon/fp_hvx.c index dee0781bd7..4543a0aa8c 100644 --- a/tests/tcg/hexagon/fp_hvx.c +++ b/tests/tcg/hexagon/fp_hvx.c @@ -29,6 +29,7 @@ int err; #define CHECK_NAN(A, DEF_NAN) (isnan(A) ? DEF_NAN : (A)) #define NAN_SF float_sf(0x7FFFFFFF) #define NAN_HF float_hf(0x7FFF) +#define NAN_BF float_hf(0x7FFF) /****************************************************************************** * Binary operations @@ -91,11 +92,43 @@ DEF_TEST_OP_2(vmpy, MULT_HF, hf, hf); #define MAX_HF(X, Y) MAX(X, Y, NAN_HF) #define MIN_SF(X, Y) MIN(X, Y, NAN_SF) #define MAX_SF(X, Y) MAX(X, Y, NAN_SF) +#define MIN_BF(X, Y) MIN(X, Y, NAN_BF) +#define MAX_BF(X, Y) MAX(X, Y, NAN_BF) DEF_TEST_OP_2(vfmin, MIN_SF, sf, sf); DEF_TEST_OP_2(vfmax, MAX_SF, sf, sf); DEF_TEST_OP_2(vfmin, MIN_HF, hf, hf); DEF_TEST_OP_2(vfmax, MAX_HF, hf, hf); +DEF_TEST_OP_2(vmin, MIN_BF, bf, bf); +DEF_TEST_OP_2(vmax, MAX_BF, bf, bf); + +#define DEF_TEST_OP_2_INTERLEAVED(vop, op, type_res, type_arg) \ + static void test_##vop##_##type_res##_##type_arg(void) \ + { \ + memset(expect, 0xff, sizeof(expect)); \ + memset(output, 0xff, sizeof(output)); \ + for (int i = 0; i < BUFSIZE / 2; i++) { \ + HVX_VectorPair *hvx_output = (HVX_VectorPair *)&output[2 * i]; \ + HVX_Vector hvx_buffer0 = *(HVX_Vector *)&buffer0[i]; \ + HVX_Vector hvx_buffer1 = *(HVX_Vector *)&buffer1[i]; \ + *hvx_output = \ + Q6_W##type_res##_##vop##_V##type_arg##V##type_arg(hvx_buffer0, \ + hvx_buffer1); \ + for (int j = 0; j < MAX_VEC_SIZE_BYTES / bytes_##type_res; j++) { \ + expect[2 * i].type_res[j] = \ + raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[2 * j]), \ + float_##type_arg(buffer1[i].type_arg[2 * j]))); \ + expect[2 * i + 1].type_res[j] = \ + raw_##type_res(op(float_##type_arg(buffer0[i].type_arg[2 * j + 1]), \ + float_##type_arg(buffer1[i].type_arg[2 * j + 1]))); \ + } \ + } \ + check_output_##type_res(__LINE__, BUFSIZE); \ + } + +DEF_TEST_OP_2_INTERLEAVED(vadd, SUM_SF, sf, bf); +DEF_TEST_OP_2_INTERLEAVED(vsub, SUB_SF, sf, bf); +DEF_TEST_OP_2_INTERLEAVED(vmpy, MULT_SF, sf, bf); /****************************************************************************** * Other tests @@ -180,6 +213,14 @@ int main(void) test_vfmax_sf_sf(); test_vfmax_hf_hf(); + /* bfloat */ + init_buffers_bf(); + test_vmin_bf_bf(); + test_vmax_bf_bf(); + test_vadd_sf_bf(); + test_vsub_sf_bf(); + test_vmpy_sf_bf(); + puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; } diff --git a/tests/tcg/hexagon/fp_hvx_cmp.c b/tests/tcg/hexagon/fp_hvx_cmp.c index 8068d1c3ee..63fb423941 100644 --- a/tests/tcg/hexagon/fp_hvx_cmp.c +++ b/tests/tcg/hexagon/fp_hvx_cmp.c @@ -22,9 +22,11 @@ int err; #define MAX_TESTS_hf (MAX_VEC_SIZE_BYTES / 2) #define MAX_TESTS_sf (MAX_VEC_SIZE_BYTES / 4) +#define MAX_TESTS_bf (MAX_VEC_SIZE_BYTES / 2) #define TRUE_MASK_sf 0xffffffff #define TRUE_MASK_hf 0xffff +#define TRUE_MASK_bf 0xffff static const char *comparisons[MAX_TESTS_sf][2]; static HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; @@ -160,6 +162,54 @@ static void test_cmp_hf(void) CHECK(hf, 2); } +static void test_cmp_bf(void) +{ + /* + * General ordering for bf: + * QNaN > SNaN > +Inf > numbers > -Inf > SNaN_neg > QNaN_neg + */ + + /* Test equality */ + PREP_TEST(); + ADD_TEST_CMP(bf, 0, 0, false); + ADD_TEST_CMP(bf, BF_SNaN, BF_SNaN, false); + CHECK(bf, 2); + + /* Common numbers */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_two, BF_one); + TEST_CMP_GT(bf, BF_one, BF_zero); + CHECK(bf, 2); + + /* Infinity vs Infinity/NaN */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_QNaN, BF_INF); + TEST_CMP_GT(bf, BF_SNaN, BF_INF); + TEST_CMP_GT(bf, BF_INF, BF_INF_neg); + TEST_CMP_GT(bf, BF_INF, BF_SNaN_neg); + TEST_CMP_GT(bf, BF_INF, BF_QNaN_neg); + TEST_CMP_GT(bf, BF_INF_neg, BF_SNaN_neg); + TEST_CMP_GT(bf, BF_INF_neg, BF_QNaN_neg); + TEST_CMP_GT(bf, BF_SNaN, BF_INF_neg); + TEST_CMP_GT(bf, BF_QNaN, BF_INF_neg); + CHECK(bf, 2); + + /* NaN vs NaN */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_QNaN, BF_SNaN); + TEST_CMP_GT(bf, BF_SNaN, BF_SNaN_neg); + TEST_CMP_GT(bf, BF_SNaN_neg, BF_QNaN_neg); + CHECK(bf, 2); + + /* NaN vs non-NaN */ + PREP_TEST(); + TEST_CMP_GT(bf, BF_QNaN, BF_one); + TEST_CMP_GT(bf, BF_SNaN, BF_one); + TEST_CMP_GT(bf, BF_one, BF_QNaN_neg); + TEST_CMP_GT(bf, BF_one, BF_SNaN_neg); + CHECK(bf, 2); +} + static void check_byte_pred(HVX_VectorPred pred, int byte_idx, uint8_t exp_mask, int line) { @@ -217,6 +267,7 @@ int main(void) test_cmp_sf(); test_cmp_hf(); + test_cmp_bf(); test_cmp_variants(); puts(err ? "FAIL" : "PASS"); diff --git a/tests/tcg/hexagon/fp_hvx_cvt.c b/tests/tcg/hexagon/fp_hvx_cvt.c index b08eba92da..7196bc9a33 100644 --- a/tests/tcg/hexagon/fp_hvx_cvt.c +++ b/tests/tcg/hexagon/fp_hvx_cvt.c @@ -19,6 +19,8 @@ int err; #include "hvx_misc.h" #include "hex_test.h" +#define NAN_BF 0x7FFF + #define TEST_EXP(TO, FROM, VAL, EXP) do { \ ((MMVector *)&buffer)->FROM[index] = VAL; \ expect[0].TO[index] = EXP; \ @@ -172,6 +174,34 @@ DEF_TEST_VCONV(sf, w, { \ TEST_EXP(sf, w, 16777219, raw_sf((float)16777220)); /* rounds UP */ \ }) +#define TEST_EXP_BF(VAL, EXP) do { \ + ((MMVector *)&buffers[1])->sf[index] = VAL; \ + ((MMVector *)&buffers[0])->sf[index] = VAL; \ + expect[0].bf[2 * index] = EXP; \ + expect[0].bf[2 * index + 1] = EXP; \ + index++; \ +} while (0) + +static void test_vconv_bf_sf(void) +{ + HVX_Vector *hvx_output = (HVX_Vector *)&output[0]; + HVX_Vector buffers[2]; + int index = 0; + memset(&buffers, 0, sizeof(buffers)); + memset(expect, 0, sizeof(expect)); + + TEST_EXP_BF(SF_QNaN, NAN_BF); + TEST_EXP_BF(SF_SNaN, NAN_BF); + TEST_EXP_BF(SF_QNaN_neg, NAN_BF); + TEST_EXP_BF(SF_INF, BF_INF); + TEST_EXP_BF(SF_INF_neg, BF_INF_neg); + TEST_EXP_BF(SF_one, BF_one); + TEST_EXP_BF(SF_zero_neg, BF_zero_neg); + + *hvx_output = Q6_Vbf_vcvt_VsfVsf(buffers[0], buffers[1]); + check_output_hf(__LINE__, 1); +} + int main(void) { test_vcvt_uh_hf(); @@ -182,6 +212,7 @@ int main(void) test_vconv_sf_w(); test_vconv_h_hf(); test_vconv_hf_h(); + test_vconv_bf_sf(); puts(err ? "FAIL" : "PASS"); return err ? 1 : 0; diff --git a/tests/tcg/hexagon/hex_test.h b/tests/tcg/hexagon/hex_test.h index 79d30ec61c..f86e6e1a69 100644 --- a/tests/tcg/hexagon/hex_test.h +++ b/tests/tcg/hexagon/hex_test.h @@ -126,6 +126,19 @@ const uint16_t HF_small_neg = 0x8010; const uint16_t HF_any = 0x3c00; const uint16_t HF_neg_two = 0xc000; +const uint16_t BF_INF = 0x7f80; +const uint16_t BF_INF_neg = 0xff80; +const uint16_t BF_QNaN = 0x7fc0; +const uint16_t BF_SNaN = 0x7f81; +const uint16_t BF_QNaN_neg = 0xffc0; +const uint16_t BF_SNaN_neg = 0xff81; +const uint16_t BF_HEX_NaN = 0x7fff; +const uint16_t BF_zero = 0x0000; +const uint16_t BF_zero_neg = 0x8000; +const uint16_t BF_one = 0x3f80; +const uint16_t BF_two = 0x4000; +const uint16_t BF_four = 0x4080; + const uint32_t SF_INF = 0x7f800000; const uint32_t SF_INF_neg = 0xff800000; const uint32_t SF_QNaN = 0x7fc00000; diff --git a/tests/tcg/hexagon/hvx_misc.h b/tests/tcg/hexagon/hvx_misc.h index 43de20da6a..c21ea975c1 100644 --- a/tests/tcg/hexagon/hvx_misc.h +++ b/tests/tcg/hexagon/hvx_misc.h @@ -41,6 +41,7 @@ typedef union { uint16_t uh[MAX_VEC_SIZE_BYTES / 2]; uint16_t hf[MAX_VEC_SIZE_BYTES / 2]; /* convenience alias */ int16_t h[MAX_VEC_SIZE_BYTES / 2]; + uint16_t bf[MAX_VEC_SIZE_BYTES / 2]; uint8_t ub[MAX_VEC_SIZE_BYTES / 1]; int8_t b[MAX_VEC_SIZE_BYTES / 1]; } MMVector; @@ -73,6 +74,7 @@ CHECK_OUTPUT_FUNC(uh, 2) CHECK_OUTPUT_FUNC(hf, 2) CHECK_OUTPUT_FUNC(ub, 1) CHECK_OUTPUT_FUNC(b, 1) +CHECK_OUTPUT_FUNC(bf, 2) static inline void init_buffers(void) { @@ -97,6 +99,12 @@ static const uint32_t FP_VALUES[] = { }; #define FP_VALUES_MAX ARRAY_SIZE(FP_VALUES) +static const uint16_t BF_VALUES[] = { + BF_INF, BF_INF_neg, BF_QNaN, BF_SNaN, BF_QNaN_neg, BF_SNaN_neg, + BF_HEX_NaN, BF_zero, BF_zero_neg, BF_one, BF_two, BF_four, +}; +#define BF_VALUES_MAX ARRAY_SIZE(BF_VALUES) + static inline void init_buffers_fp(void) { _Static_assert(BUFSIZE * (MAX_VEC_SIZE_BYTES / 4) > @@ -116,6 +124,25 @@ static inline void init_buffers_fp(void) } } +static inline void init_buffers_bf(void) +{ + _Static_assert(BUFSIZE * (MAX_VEC_SIZE_BYTES / 2) > + BF_VALUES_MAX * BF_VALUES_MAX, + "test arrays can't fit all BF_VALUES combinations"); + int counter1 = 0, counter2 = 0; + for (int i = 0; i < BUFSIZE; i++) { + for (int j = 0; j < MAX_VEC_SIZE_BYTES / 2; j++) { + buffer0[i].bf[j] = BF_VALUES[counter1]; + buffer1[i].bf[j] = BF_VALUES[counter2]; + counter2++; + if (counter2 == BF_VALUES_MAX) { + counter2 = 0; + counter1 = (counter1 + 1) % BF_VALUES_MAX; + } + } + } +} + #define VEC_OP1(ASM, EL, IN, OUT) \ asm("v2 = vmem(%0 + #0)\n\t" \ "v2" #EL " = " #ASM "(v2" #EL ")\n\t" \ @@ -212,10 +239,13 @@ static inline void test_##NAME(bool invert) \ #define float_sf(x) ({ typeof(x) _x = (x); *((float *)&(_x)); }) #define float_hf(x) ({ typeof(x) _x = (x); *((_Float16 *) &(_x)); }) +#define float_bf(x) ({ uint32_t _u = ((uint32_t)(x)) << 16; *((float *)&(_u)); }) #define raw_sf(x) ({ typeof(x) _x = (x); *((uint32_t *)&(_x)); }) #define raw_hf(x) ({ typeof(x) _x = (x); *((uint16_t *)&(_x)); }) +#define raw_bf(x) ({ typeof(x) _x = (x); (uint16_t)(*((uint32_t *)&(_x)) >> 16); }) #define float_hf_to_sf(x) ((float)x) #define bytes_hf 2 #define bytes_sf 4 +#define bytes_bf 2 #endif From 21e726d226e53896a0e6dcbb732b93bcea89115e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 437/860] target/arm: Implement FTMOPA (non-widening, FP32) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 ++++ target/arm/tcg/helper-sme-defs.h | 2 ++ target/arm/tcg/sme.decode | 10 +++++++ target/arm/tcg/sme_helper.c | 49 ++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 26 +++++++++++++++++ 5 files changed, 92 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index fb5ed25ad0..6370c03e2a 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1590,6 +1590,11 @@ static inline bool isar_feature_aa64_sme_mop4(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64SMFR0, SMOP4); } +static inline bool isar_feature_aa64_sme_tmop(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, STMOP); +} + static inline bool isar_feature_aa64_ssve_aes(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64SMFR0, AES); diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index 7fc7129e54..8107eb40af 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -404,3 +404,5 @@ DEF_HELPER_FLAGS_4(sme_usmop4a_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4s_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4a_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4s_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sme_ftmopa_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 53e7e67feb..0250177d0d 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1131,3 +1131,13 @@ UMOP4_dh 1010 0001 111. ...0 0000 00.. ..0. 1... @mop4_o3 USMOP4_sb 1000 0001 000. ...0 1000 00.. ..0. 00.. @mop4_o2 USMOP4_dh 1010 0001 110. ...0 0000 00.. ..0. 1... @mop4_o3 + +# SME TMOP Sparse outer products + +&tmop zad zn zm zk idx +%tmop_zk 10:3 !function=expand_tmop_zk + +@tmop_o2 .... .... ... zm:5 ... ... .... idx:2 .. zad:2 \ + &tmop zn=%zn_ax2 zk=%tmop_zk + +FTMOPA_ss 1000 0000 010 ..... 000 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 23bb816b6a..571b0add34 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2636,6 +2636,49 @@ void sme_mop4(void *vza, void *vzn, void *vzm, void *fn_opaque, } } +/* + * Sparse outer product, non-widening. ESZ in {16, 32}. + */ +static void sme_tmop(void *vza, void *vzn, void *vzm, uint64_t *zk, + void *fn_opaque, uint32_t desc, MemOp esz, + void (*fn)(void *, void *, void *, void *)) +{ + intptr_t oprsz = simd_maxsz(desc); + intptr_t index = simd_data(desc); + intptr_t esize = 1 << esz; + intptr_t host_adj = HOST_BIG_ENDIAN ? 8 - esize : 0; + /* Base in bits for op3[index*:csize], csize = (VL * 2) / esize. */ + intptr_t ctrl_base = index * oprsz * 2; + /* Create a zero for use with the largest esz. */ + uint32_t zero = 0; + + for (intptr_t row = 0; row < oprsz; row += esize) { + void *vza_row = vza + tile_vslice_offset(row); + + for (intptr_t col = 0; col < oprsz; col += esize) { + void *e2 = vzm + (col ^ host_adj); + void *e3 = vza_row + (col ^ host_adj); + + /* + * Two control bits select one element: + * Zn[row], if [0] is set, + * Zn+1[row], if [1] is set, + * 0, otherwise. + * Compute the address of that element. + */ + void *e1 = &zero; + uint64_t this_ctrl = extractn(zk, (ctrl_base + 2 * col) >> esz, 2); + if (this_ctrl) { + e1 = vzn + (row ^ host_adj); + if (!(this_ctrl & 1)) { + e1 += sizeof(ARMVectorReg); + } + } + fn(e3, e1, e2, fn_opaque); + } + } +} + static void inner_fmop4a_hh(void *vd, void *vn, void *vm, void *vinfo) { float16 *d = vd, *n = vn, *m = vm; @@ -2692,6 +2735,12 @@ void HELPER(sme_fmop4a_ss)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, fpst, desc, sizeof(float32), inner_fmop4a_ss); } +void HELPER(sme_ftmopa_ss)(void *vza, void *vzn, void *vzm, void *vzk, + float_status *fpst, uint32_t desc) +{ + sme_tmop(vza, vzn, vzm, vzk, fpst, desc, MO_32, inner_fmop4a_ss); +} + static void inner_fmop4s_ss(void *vd, void *vn, void *vm, void *vinfo) { float32 *d = vd, *n = vn, *m = vm; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 0794794e3d..3cfd52dca4 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -31,6 +31,12 @@ * Include the generated decoder. */ +static int expand_tmop_zk(DisasContext *s, int x) +{ + /* Pseudocode for 1:K:1:zk. */ + return 0b10100 | ((x & 4) << 1) | (x & 3); +} + #include "decode-sme.c.inc" static bool sme2_zt0_enabled_check(DisasContext *s) @@ -2132,3 +2138,23 @@ TRANS_FEAT(USMOP4_sb, aa64_sme_mop4, do_mop4_int, a, MO_32, a->s ? gen_helper_sme_usmop4s_sb : gen_helper_sme_usmop4a_sb) TRANS_FEAT(USMOP4_dh, aa64_sme_mop4_i16i64, do_mop4_int, a, MO_64, a->s ? gen_helper_sme_usmop4s_dh : gen_helper_sme_usmop4a_dh) + +static bool do_tmop_fp(DisasContext *s, arg_tmop *a, MemOp esz, + int e_fpst, gen_helper_gvec_4_ptr *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->idx); + TCGv_ptr za = get_tile(s, esz, a->zad); + TCGv_ptr zn = vec_full_reg_ptr(s, a->zn); + TCGv_ptr zm = vec_full_reg_ptr(s, a->zm); + TCGv_ptr zk = vec_full_reg_ptr(s, a->zm); + TCGv_ptr fpst = (e_fpst >= 0 ? fpstatus_ptr(e_fpst) : tcg_env); + + fn(za, zn, zm, zk, fpst, tcg_constant_i32(desc)); + } + return true; +} + +TRANS_FEAT(FTMOPA_ss, aa64_sme_tmop, do_tmop_fp, + a, MO_32, FPST_ZA, gen_helper_sme_ftmopa_ss) From 76bc7dc82d2c37295aca07757e7623d3bfc650a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 438/860] target/arm: Implement FTMOPA (non-widening, FP16) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 +++++ target/arm/tcg/helper-sme-defs.h | 1 + target/arm/tcg/sme.decode | 3 +++ target/arm/tcg/sme_helper.c | 6 ++++++ target/arm/tcg/translate-sme.c | 2 ++ 5 files changed, 17 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 6370c03e2a..4190398a1d 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1842,6 +1842,11 @@ static inline bool isar_feature_aa64_sme_mop4_i16i64(const ARMISARegisters *id) return isar_feature_aa64_sme_mop4(id) && isar_feature_aa64_sme_i16i64(id); } +static inline bool isar_feature_aa64_sme_tmop_f16f16(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f16f16(id); +} + /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index 8107eb40af..84707e6c71 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -405,4 +405,5 @@ DEF_HELPER_FLAGS_4(sme_usmop4s_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4a_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4s_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_ftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sme_ftmopa_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 0250177d0d..16ed3ca391 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1137,7 +1137,10 @@ USMOP4_dh 1010 0001 110. ...0 0000 00.. ..0. 1... @mop4_o3 &tmop zad zn zm zk idx %tmop_zk 10:3 !function=expand_tmop_zk +@tmop_o1 .... .... ... zm:5 ... ... .... idx:2 ... zad:1 \ + &tmop zn=%zn_ax2 zk=%tmop_zk @tmop_o2 .... .... ... zm:5 ... ... .... idx:2 .. zad:2 \ &tmop zn=%zn_ax2 zk=%tmop_zk +FTMOPA_hh 1000 0001 010 ..... 000 ... .... .. 100 . @tmop_o1 FTMOPA_ss 1000 0000 010 ..... 000 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 571b0add34..49fd3d07e4 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2693,6 +2693,12 @@ void HELPER(sme_fmop4a_hh)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, fpst, desc, sizeof(float16), inner_fmop4a_hh); } +void HELPER(sme_ftmopa_hh)(void *vza, void *vzn, void *vzm, void *vzk, + float_status *fpst, uint32_t desc) +{ + sme_tmop(vza, vzn, vzm, vzk, fpst, desc, MO_16, inner_fmop4a_hh); +} + static void inner_fmop4s_hh(void *vd, void *vn, void *vm, void *vinfo) { float16 *d = vd, *n = vn, *m = vm; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 3cfd52dca4..5ad59ab99b 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2156,5 +2156,7 @@ static bool do_tmop_fp(DisasContext *s, arg_tmop *a, MemOp esz, return true; } +TRANS_FEAT(FTMOPA_hh, aa64_sme_tmop_f16f16, do_tmop_fp, + a, MO_16, FPST_ZA_F16, gen_helper_sme_ftmopa_hh) TRANS_FEAT(FTMOPA_ss, aa64_sme_tmop, do_tmop_fp, a, MO_32, FPST_ZA, gen_helper_sme_ftmopa_ss) From aa8965de49e66cc4c04dbd99274d108418687a8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 439/860] target/arm: Implement BFTMOPA (non-widening) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 +++++ target/arm/tcg/helper-sme-defs.h | 1 + target/arm/tcg/sme.decode | 1 + target/arm/tcg/sme_helper.c | 6 ++++++ target/arm/tcg/translate-sme.c | 2 ++ 5 files changed, 15 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 4190398a1d..d3bf0746cb 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1842,6 +1842,11 @@ static inline bool isar_feature_aa64_sme_mop4_i16i64(const ARMISARegisters *id) return isar_feature_aa64_sme_mop4(id) && isar_feature_aa64_sme_i16i64(id); } +static inline bool isar_feature_aa64_sme_tmop_b16b16(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_b16b16(id); +} + static inline bool isar_feature_aa64_sme_tmop_f16f16(const ARMISARegisters *id) { return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f16f16(id); diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index 84707e6c71..224de45294 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -405,5 +405,6 @@ DEF_HELPER_FLAGS_4(sme_usmop4s_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4a_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_usmop4s_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme_bftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sme_ftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sme_ftmopa_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 16ed3ca391..f0f9b3f61d 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1142,5 +1142,6 @@ USMOP4_dh 1010 0001 110. ...0 0000 00.. ..0. 1... @mop4_o3 @tmop_o2 .... .... ... zm:5 ... ... .... idx:2 .. zad:2 \ &tmop zn=%zn_ax2 zk=%tmop_zk +BFTMOPA_hh 1000 0001 011 ..... 000 ... .... .. 100 . @tmop_o1 FTMOPA_hh 1000 0001 010 ..... 000 ... .... .. 100 . @tmop_o1 FTMOPA_ss 1000 0000 010 ..... 000 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 49fd3d07e4..e57c56d5f2 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2831,6 +2831,12 @@ void HELPER(sme_bfmop4a_hh)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, fpst, desc, sizeof(bfloat16), inner_bfmop4a_hh); } +void HELPER(sme_bftmopa_hh)(void *vza, void *vzn, void *vzm, void *vzk, + float_status *fpst, uint32_t desc) +{ + sme_tmop(vza, vzn, vzm, vzk, fpst, desc, MO_16, inner_bfmop4a_hh); +} + static void inner_bfmop4s_hh(void *vd, void *vn, void *vm, void *vinfo) { bfloat16 *d = vd, *n = vn, *m = vm; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 5ad59ab99b..04e1550c05 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2156,6 +2156,8 @@ static bool do_tmop_fp(DisasContext *s, arg_tmop *a, MemOp esz, return true; } +TRANS_FEAT(BFTMOPA_hh, aa64_sme_tmop_b16b16, do_tmop_fp, + a, MO_16, FPST_ZA, gen_helper_sme_bftmopa_hh) TRANS_FEAT(FTMOPA_hh, aa64_sme_tmop_f16f16, do_tmop_fp, a, MO_16, FPST_ZA_F16, gen_helper_sme_ftmopa_hh) TRANS_FEAT(FTMOPA_ss, aa64_sme_tmop, do_tmop_fp, From cfe1e6503a0b828b7237d5c16c3b7f59f5bd7449 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 440/860] target/arm: Implement BFTMOPA (widening) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme-defs.h | 2 ++ target/arm/tcg/sme.decode | 2 ++ target/arm/tcg/sme_helper.c | 54 ++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 3 ++ 4 files changed, 61 insertions(+) diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index 224de45294..f05b53f412 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -408,3 +408,5 @@ DEF_HELPER_FLAGS_4(sme_usmop4s_dh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sme_bftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sme_ftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sme_ftmopa_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(sme_bftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index f0f9b3f61d..49af8fd19e 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1145,3 +1145,5 @@ USMOP4_dh 1010 0001 110. ...0 0000 00.. ..0. 1... @mop4_o3 BFTMOPA_hh 1000 0001 011 ..... 000 ... .... .. 100 . @tmop_o1 FTMOPA_hh 1000 0001 010 ..... 000 ... .... .. 100 . @tmop_o1 FTMOPA_ss 1000 0000 010 ..... 000 ... .... .. 00 .. @tmop_o2 + +BFTMOPA_sh 1000 0001 010 ..... 000 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index e57c56d5f2..91447d9c0e 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2679,6 +2679,50 @@ static void sme_tmop(void *vza, void *vzn, void *vzm, uint64_t *zk, } } +/* + * Sparse outer product, widening 2-way, 16 to 32-bit. + */ +static void sme_tmop_2way_sh(uint32_t *za, uint16_t *zn0, uint32_t *zm, + uint64_t *zk, void *fn_opaque, uint32_t desc, + void (*fn)(void *, void *, void *, void *)) +{ + intptr_t oprsz = simd_maxsz(desc); + intptr_t dim = oprsz >> MO_32; + intptr_t index = simd_data(desc); + intptr_t ctrl_base = (index * oprsz) >> 1; + uint16_t *zn1 = zn0 + sizeof(ARMVectorReg) / 2; + + for (intptr_t row = 0; row < dim; row++) { + uint32_t *za_row = za + tile_vslice_offset(row); + + for (intptr_t col = 0; col < dim; col++) { + uint32_t *e2 = zm + H4(col); + uint32_t *e3 = za_row + H4(col); + uint32_t e1 = 0; + + /* + * Four control bits select two elements. The two elements + * may be non-contiguous, so assemble them locally into e1. + * Pseudo-code has a double loop running forward, with a + * test for (i < 2) to limit construction to 2 elements. + * Easier to run a single loop backward, shifting extra + * elements off the top of our uint32_t. + */ + uint64_t this_ctrl = extractn(zk, ctrl_base + col * 4, 4); + for (int i = 3; i >= 0; i--) { + if (this_ctrl & (1 << i)) { + bool e = i & 1; + bool r = i & 2; + uint16_t *p = (r ? zn1 : zn0) + H2(2 * row + e); + e1 = (e1 << 16) | *p; + } + } + + fn(e3, &e1, e2, fn_opaque); + } + } +} + static void inner_fmop4a_hh(void *vd, void *vn, void *vm, void *vinfo) { float16 *d = vd, *n = vn, *m = vm; @@ -2893,6 +2937,16 @@ void HELPER(sme_bfmop4a_sh)(void *vza, void *vzn, void *vzm, : inner_bfmop4a_sh); } +void HELPER(sme_bftmopa_sh)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + float_status fpst; + + sme_tmop_2way_sh(vza, vzn, vzm, vzk, &fpst, desc, + is_ebf(env, &fpst) ? inner_ebf_bfmop4a_sh + : inner_bfmop4a_sh); +} + static void inner_bfmop4s_sh(void *vd, void *vn, void *vm, void *vinfo) { float32 *d = vd; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 04e1550c05..66bba48d99 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2162,3 +2162,6 @@ TRANS_FEAT(FTMOPA_hh, aa64_sme_tmop_f16f16, do_tmop_fp, a, MO_16, FPST_ZA_F16, gen_helper_sme_ftmopa_hh) TRANS_FEAT(FTMOPA_ss, aa64_sme_tmop, do_tmop_fp, a, MO_32, FPST_ZA, gen_helper_sme_ftmopa_ss) + +TRANS_FEAT(BFTMOPA_sh, aa64_sme_tmop, do_tmop_fp, + a, MO_32, FPST_ENV, gen_helper_sme_bftmopa_sh) From f6a2d780f1489de6f3453e0c0ef70ae9813a412c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 441/860] target/arm: Implement FTMOPA (widening, 2-way, FP16 to FP32) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme-defs.h | 1 + target/arm/tcg/sme.decode | 1 + target/arm/tcg/sme_helper.c | 6 ++++++ target/arm/tcg/translate-sme.c | 2 ++ 4 files changed, 10 insertions(+) diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index f05b53f412..8a959447b2 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -410,3 +410,4 @@ DEF_HELPER_FLAGS_6(sme_ftmopa_hh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fps DEF_HELPER_FLAGS_6(sme_ftmopa_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sme_bftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sme_ftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 49af8fd19e..5c453ea729 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1147,3 +1147,4 @@ FTMOPA_hh 1000 0001 010 ..... 000 ... .... .. 100 . @tmop_o1 FTMOPA_ss 1000 0000 010 ..... 000 ... .... .. 00 .. @tmop_o2 BFTMOPA_sh 1000 0001 010 ..... 000 ... .... .. 00 .. @tmop_o2 +FTMOPA_sh 1000 0001 011 ..... 000 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 91447d9c0e..c2b7c23d6c 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -3020,6 +3020,12 @@ void HELPER(sme_fmop4a_sh)(void *vza, void *vzn, void *vzm, sme_mop4(vza, vzn, vzm, env, desc, sizeof(float32), inner_fmop4a_sh); } +void HELPER(sme_ftmopa_sh)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + sme_tmop_2way_sh(vza, vzn, vzm, vzk, env, desc, inner_fmop4a_sh); +} + static void inner_fmop4s_sh(void *vd, void *vn, void *vm, void *vinfo) { float32 *d = vd; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 66bba48d99..8a55b79727 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2165,3 +2165,5 @@ TRANS_FEAT(FTMOPA_ss, aa64_sme_tmop, do_tmop_fp, TRANS_FEAT(BFTMOPA_sh, aa64_sme_tmop, do_tmop_fp, a, MO_32, FPST_ENV, gen_helper_sme_bftmopa_sh) +TRANS_FEAT(FTMOPA_sh, aa64_sme_tmop, do_tmop_fp, + a, MO_32, FPST_ENV, gen_helper_sme_ftmopa_sh) From e921da8b98f0be99edf973e3d623d79ac2cd3705 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 442/860] target/arm: Implement FTMOPA (widening, 2-way, FP8 to FP16) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 ++++ target/arm/tcg/fp8_helper.c | 43 ++++++++++++++++++++++++++++++++ target/arm/tcg/helper-fp8-defs.h | 2 ++ target/arm/tcg/sme.decode | 2 ++ target/arm/tcg/translate-sme.c | 12 +++++++++ 5 files changed, 64 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index d3bf0746cb..1ab6c90b8c 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1852,6 +1852,11 @@ static inline bool isar_feature_aa64_sme_tmop_f16f16(const ARMISARegisters *id) return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f16f16(id); } +static inline bool isar_feature_aa64_sme_tmop_f8f16(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f8f16(id); +} + /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ diff --git a/target/arm/tcg/fp8_helper.c b/target/arm/tcg/fp8_helper.c index 4b046f5f26..305cafdc9b 100644 --- a/target/arm/tcg/fp8_helper.c +++ b/target/arm/tcg/fp8_helper.c @@ -1005,3 +1005,46 @@ void HELPER(sme_fmop4a_hb)(void *vza, void *vzn, void *vzm, FP8MulContext ctx = fp8_mul_start(env, 0xf); sme_mop4(vza, vzn, vzm, &ctx, desc, sizeof(float16), inner_fmop4a_hb); } + +void HELPER(sme_ftmopa_hb)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + FP8MulContext ctx = fp8_mul_start(env, 0xf); + intptr_t oprsz = simd_maxsz(desc); + intptr_t dim = oprsz >> MO_16; + intptr_t index = simd_data(desc); + intptr_t ctrl_base = (index * oprsz) >> 1; + uint8_t *zn0 = vzn, *zn1 = vzn + sizeof(ARMVectorReg); + uint16_t *za = vza, *zm = vzm; + uint64_t *zk = vzk; + + for (intptr_t row = 0; row < dim; row++) { + uint16_t *za_row = za + tile_vslice_offset(row); + + for (intptr_t col = 0; col < dim; col++) { + uint16_t e2 = zm[H2(col)]; + uint16_t *e3 = za_row + H2(col); + uint16_t e1 = 0; + + /* + * Four control bits select two elements. The two elements + * may be non-contiguous, so assemble them locally into e1. + * Pseudo-code has a double loop running forward, with a + * test for (i < 2) to limit construction to 2 elements. + * Easier to run a single loop backward, shifting extra + * elements off the top of our uint16_t. + */ + uint64_t this_ctrl = extractn(zk, ctrl_base + col * 4, 4); + for (int i = 3; i >= 0; i--) { + if (this_ctrl & (1 << i)) { + bool e = i & 1; + bool r = i & 2; + uint8_t *p = (r ? zn1 : zn0) + H1(2 * row + e); + e1 = (e1 << 8) | *p; + } + } + + *e3 = f8dotadd_h(e1, e2, 2, *e3, &ctx); + } + } +} diff --git a/target/arm/tcg/helper-fp8-defs.h b/target/arm/tcg/helper-fp8-defs.h index dedbd8541c..13be62aa15 100644 --- a/target/arm/tcg/helper-fp8-defs.h +++ b/target/arm/tcg/helper-fp8-defs.h @@ -47,3 +47,5 @@ DEF_HELPER_FLAGS_5(sme_fvdot_idx_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, DEF_HELPER_FLAGS_5(sme_fmop4a_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_5(sme_fmop4a_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_6(sme_ftmopa_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 5c453ea729..da6f4686d3 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1148,3 +1148,5 @@ FTMOPA_ss 1000 0000 010 ..... 000 ... .... .. 00 .. @tmop_o2 BFTMOPA_sh 1000 0001 010 ..... 000 ... .... .. 00 .. @tmop_o2 FTMOPA_sh 1000 0001 011 ..... 000 ... .... .. 00 .. @tmop_o2 + +FTMOPA_hb 1000 0000 011 ..... 000 ... .... .. 100 . @tmop_o1 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 8a55b79727..206f078bfb 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2167,3 +2167,15 @@ TRANS_FEAT(BFTMOPA_sh, aa64_sme_tmop, do_tmop_fp, a, MO_32, FPST_ENV, gen_helper_sme_bftmopa_sh) TRANS_FEAT(FTMOPA_sh, aa64_sme_tmop, do_tmop_fp, a, MO_32, FPST_ENV, gen_helper_sme_ftmopa_sh) + +static bool do_tmop_fp8(DisasContext *s, arg_tmop *a, MemOp esz, + gen_helper_gvec_4_ptr *fn) +{ + if (!fpmr_access_check(s)) { + return true; + } + return do_tmop_fp(s, a, esz, FPST_ENV, fn); +} + +TRANS_FEAT(FTMOPA_hb, aa64_sme_tmop_f8f16, do_tmop_fp8, + a, MO_16, gen_helper_sme_ftmopa_hb) From 1c7b4f9db3ed05a09431e3e1363b4af47c28df2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 443/860] target/arm: Implement FTMOPA (widening, 4-way, FP8 to FP32) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 ++++ target/arm/tcg/fp8_helper.c | 7 +++++ target/arm/tcg/helper-fp8-defs.h | 1 + target/arm/tcg/sme.decode | 1 + target/arm/tcg/sme_helper.c | 44 ++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 2 ++ target/arm/tcg/vec_internal.h | 7 +++++ 7 files changed, 67 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 1ab6c90b8c..280ead6f9c 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -1857,6 +1857,11 @@ static inline bool isar_feature_aa64_sme_tmop_f8f16(const ARMISARegisters *id) return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f8f16(id); } +static inline bool isar_feature_aa64_sme_tmop_f8f32(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme_tmop(id) && isar_feature_aa64_sme_f8f32(id); +} + /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ diff --git a/target/arm/tcg/fp8_helper.c b/target/arm/tcg/fp8_helper.c index 305cafdc9b..0be811e71b 100644 --- a/target/arm/tcg/fp8_helper.c +++ b/target/arm/tcg/fp8_helper.c @@ -1048,3 +1048,10 @@ void HELPER(sme_ftmopa_hb)(void *vza, void *vzn, void *vzm, void *vzk, } } } + +void HELPER(sme_ftmopa_sb)(void *vza, void *vzn, void *vzm, void *vzk, + CPUArchState *env, uint32_t desc) +{ + FP8MulContext ctx = fp8_mul_start(env, 0xf); + sme_tmop_4way_sb(vza, vzn, vzm, vzk, &ctx, desc, inner_fmop4a_sb); +} diff --git a/target/arm/tcg/helper-fp8-defs.h b/target/arm/tcg/helper-fp8-defs.h index 13be62aa15..e2cf2ba8c7 100644 --- a/target/arm/tcg/helper-fp8-defs.h +++ b/target/arm/tcg/helper-fp8-defs.h @@ -49,3 +49,4 @@ DEF_HELPER_FLAGS_5(sme_fmop4a_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32 DEF_HELPER_FLAGS_5(sme_fmop4a_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sme_ftmopa_hb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sme_ftmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index da6f4686d3..eab1f7e8fb 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1150,3 +1150,4 @@ BFTMOPA_sh 1000 0001 010 ..... 000 ... .... .. 00 .. @tmop_o2 FTMOPA_sh 1000 0001 011 ..... 000 ... .... .. 00 .. @tmop_o2 FTMOPA_hb 1000 0000 011 ..... 000 ... .... .. 100 . @tmop_o1 +FTMOPA_sb 1000 0000 011 ..... 000 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index c2b7c23d6c..3d7aefac5b 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2723,6 +2723,50 @@ static void sme_tmop_2way_sh(uint32_t *za, uint16_t *zn0, uint32_t *zm, } } +void sme_tmop_4way_sb(uint32_t *za, uint8_t *zn0, uint32_t *zm, + uint64_t *zk, void *fn_opaque, uint32_t desc, + void (*fn)(void *, void *, void *, void *)) +{ + intptr_t oprsz = simd_maxsz(desc); + intptr_t dim = oprsz >> MO_32; + intptr_t index = simd_data(desc); + intptr_t ctrl_base = (index * oprsz) >> 1; + uint8_t *zn1 = zn0 + sizeof(ARMVectorReg); + + for (intptr_t row = 0; row < dim; row++) { + uint32_t *za_row = za + tile_vslice_offset(row); + + for (intptr_t col = 0; col < dim; col++) { + uint32_t *e2 = zm + H4(col); + uint32_t *e3 = za_row + H4(col); + uint16_t e1l = 0, e1h = 0; + uint32_t e1; + + /* + * Eight control bits select two elements from each row. + * The elements may be non-contiguous, so assemble them + * locally into e1. + * Pseudo-code has a triple loop running forward, with a + * test for (i < 2) to limit construction to 2 elements. + * Easier to run a single loop backward, shifting extra + * elements off the top. + */ + uint64_t this_ctrl = extractn(zk, ctrl_base + col * 8, 8); + for (int e = 3; e >= 0; e--) { + if (this_ctrl & (0x01 << e)) { + e1l = (e1l << 8) | zn0[H1(4 * row + e)]; + } + if (this_ctrl & (0x10 << e)) { + e1h = (e1h << 8) | zn1[H1(4 * row + e)]; + } + } + e1 = (e1h << 16) | e1l; + + fn(e3, &e1, e2, fn_opaque); + } + } +} + static void inner_fmop4a_hh(void *vd, void *vn, void *vm, void *vinfo) { float16 *d = vd, *n = vn, *m = vm; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 206f078bfb..ceed864da8 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2179,3 +2179,5 @@ static bool do_tmop_fp8(DisasContext *s, arg_tmop *a, MemOp esz, TRANS_FEAT(FTMOPA_hb, aa64_sme_tmop_f8f16, do_tmop_fp8, a, MO_16, gen_helper_sme_ftmopa_hb) +TRANS_FEAT(FTMOPA_sb, aa64_sme_tmop_f8f32, do_tmop_fp8, + a, MO_32, gen_helper_sme_ftmopa_sb) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 038a2a3439..8f6d8a984b 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -555,4 +555,11 @@ void sme_mop4(void *vza, void *vzn, void *vzm, void *fn_opaque, uint32_t desc, size_t esize, void (*fn)(void *, void *, void *, void *)); +/* + * Perform SME sparse outer product, 4-way, 8 to 32-bit. + */ +void sme_tmop_4way_sb(uint32_t *za, uint8_t *zn0, uint32_t *zm, + uint64_t *zk, void *fn_opaque, uint32_t desc, + void (*fn)(void *, void *, void *, void *)); + #endif /* TARGET_ARM_VEC_INTERNAL_H */ From 695e4e87a437e1d2d50755b0ed906cc54898813c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 444/860] target/arm: Implement [SU]TMOPA (2-way) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme-defs.h | 3 +++ target/arm/tcg/sme.decode | 3 +++ target/arm/tcg/sme_helper.c | 12 ++++++++++++ target/arm/tcg/translate-sme.c | 21 +++++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index 8a959447b2..1de72b7472 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -411,3 +411,6 @@ DEF_HELPER_FLAGS_6(sme_ftmopa_ss, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fps DEF_HELPER_FLAGS_6(sme_bftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sme_ftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(sme_stmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_utmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index eab1f7e8fb..08036c60c0 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1151,3 +1151,6 @@ FTMOPA_sh 1000 0001 011 ..... 000 ... .... .. 00 .. @tmop_o2 FTMOPA_hb 1000 0000 011 ..... 000 ... .... .. 100 . @tmop_o1 FTMOPA_sb 1000 0000 011 ..... 000 ... .... .. 00 .. @tmop_o2 + +STMOPA_sh 1000 0000 010 ..... 100 ... .... .. 10 .. @tmop_o2 +UTMOPA_sh 1000 0001 010 ..... 100 ... .... .. 10 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 3d7aefac5b..951cbbf58e 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -3123,6 +3123,18 @@ IMOP4_2WAY(umop4s_sh, -, int32_t, uint16_t, uint16_t) #undef IMOP4_2WAY +#define ITMOP_2WAY(TNAME, MNAME) \ +void HELPER(sme_##TNAME)(void *vza, void *vzn, void *vzm, \ + void *vzk, uint32_t desc) \ +{ \ + sme_tmop_2way_sh(vza, vzn, vzm, vzk, NULL, desc, inner_##MNAME); \ +} + +ITMOP_2WAY(stmopa_sh, smop4a_sh) +ITMOP_2WAY(utmopa_sh, umop4a_sh) + +#undef ITMOP_2WAY + #define IMOP4_4WAY(NAME, OP, TYPED, TYPEN, TYPEM) \ static void inner_##NAME(void *vd, void *vn, void *vm, void *vinfo) \ { \ diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index ceed864da8..abdf98a40c 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2181,3 +2181,24 @@ TRANS_FEAT(FTMOPA_hb, aa64_sme_tmop_f8f16, do_tmop_fp8, a, MO_16, gen_helper_sme_ftmopa_hb) TRANS_FEAT(FTMOPA_sb, aa64_sme_tmop_f8f32, do_tmop_fp8, a, MO_32, gen_helper_sme_ftmopa_sb) + +static bool do_tmop_int(DisasContext *s, arg_tmop *a, MemOp esz, + gen_helper_gvec_4 *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->idx); + TCGv_ptr za = get_tile(s, esz, a->zad); + TCGv_ptr zn = vec_full_reg_ptr(s, a->zn); + TCGv_ptr zm = vec_full_reg_ptr(s, a->zm); + TCGv_ptr zk = vec_full_reg_ptr(s, a->zm); + + fn(za, zn, zm, zk, tcg_constant_i32(desc)); + } + return true; +} + +TRANS_FEAT(STMOPA_sh, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_stmopa_sh) +TRANS_FEAT(UTMOPA_sh, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_utmopa_sh) From e56b670ea130b05c6abd3218357996a14c88338d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 445/860] target/arm: Implement {S,SU,US,U}TMOPA (4-way) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme-defs.h | 5 +++++ target/arm/tcg/sme.decode | 5 +++++ target/arm/tcg/sme_helper.c | 14 ++++++++++++++ target/arm/tcg/translate-sme.c | 9 +++++++++ 4 files changed, 33 insertions(+) diff --git a/target/arm/tcg/helper-sme-defs.h b/target/arm/tcg/helper-sme-defs.h index 1de72b7472..b8cb643367 100644 --- a/target/arm/tcg/helper-sme-defs.h +++ b/target/arm/tcg/helper-sme-defs.h @@ -414,3 +414,8 @@ DEF_HELPER_FLAGS_6(sme_ftmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env DEF_HELPER_FLAGS_5(sme_stmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme_utmopa_sh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sme_stmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_sutmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_utmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme_ustmopa_sb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 08036c60c0..e8176fffdb 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -1154,3 +1154,8 @@ FTMOPA_sb 1000 0000 011 ..... 000 ... .... .. 00 .. @tmop_o2 STMOPA_sh 1000 0000 010 ..... 100 ... .... .. 10 .. @tmop_o2 UTMOPA_sh 1000 0001 010 ..... 100 ... .... .. 10 .. @tmop_o2 + +STMOPA_sb 1000 0000 010 ..... 100 ... .... .. 00 .. @tmop_o2 +SUTMOPA_sb 1000 0000 011 ..... 100 ... .... .. 00 .. @tmop_o2 +USTMOPA_sb 1000 0001 010 ..... 100 ... .... .. 00 .. @tmop_o2 +UTMOPA_sb 1000 0001 011 ..... 100 ... .... .. 00 .. @tmop_o2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 951cbbf58e..9de95428d2 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -3168,3 +3168,17 @@ IMOP4_4WAY(usmop4a_dh, +, int64_t, uint16_t, int16_t) IMOP4_4WAY(usmop4s_dh, -, int64_t, uint16_t, int16_t) #undef IMOP4_4WAY + +#define ITMOP_4WAY(TNAME, MNAME) \ +void HELPER(sme_##TNAME)(void *vza, void *vzn, void *vzm, \ + void *vzk, uint32_t desc) \ +{ \ + sme_tmop_4way_sb(vza, vzn, vzm, vzk, NULL, desc, inner_##MNAME); \ +} + +ITMOP_4WAY(stmopa_sb, smop4a_sb) +ITMOP_4WAY(utmopa_sb, umop4a_sb) +ITMOP_4WAY(sutmopa_sb, sumop4a_sb) +ITMOP_4WAY(ustmopa_sb, usmop4a_sb) + +#undef ITMOP_4WAY diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index abdf98a40c..768b76f01e 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -2202,3 +2202,12 @@ TRANS_FEAT(STMOPA_sh, aa64_sme_tmop, do_tmop_int, a, MO_32, gen_helper_sme_stmopa_sh) TRANS_FEAT(UTMOPA_sh, aa64_sme_tmop, do_tmop_int, a, MO_32, gen_helper_sme_utmopa_sh) + +TRANS_FEAT(STMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_stmopa_sb) +TRANS_FEAT(SUTMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_sutmopa_sb) +TRANS_FEAT(USTMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_ustmopa_sb) +TRANS_FEAT(UTMOPA_sb, aa64_sme_tmop, do_tmop_int, + a, MO_32, gen_helper_sme_utmopa_sb) From 8ae365bdc8395e54fe0e137143858b505409d82a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 446/860] target/arm: Enable FEAT_SME_TMOP for -cpu max Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260713230244.70174-11-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 | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 9930974a50..cc42db9e0b 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -188,6 +188,7 @@ the following architecture extensions: - FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions) - FEAT_SME_LUTv2 (Lookup table instructions with 4-bit indices and 8-bit elements) - FEAT_SME_MOP4 (Quarter-tile outer product instructions) +- FEAT_SME_TMOP (Structured sparsity outer product instructions) - FEAT_SSVE_AES (Streaming SVE Mode Advanced Encryption Standard and 128-bit polynomial multiply long instructions) - FEAT_SSVE_FEXPA (Streaming FEXPA instruction) - FEAT_SSVE_FP8DOT2 (SVE2 FP8 2-way dot product to half-precision instructions in Streaming SVE mode) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 64e25a04e3..48be49580a 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -177,6 +177,7 @@ abi_ulong get_elf_hwcap(CPUState *cs) GET_FEATURE_ID(aa64_ssve_fexpa, ARM_HWCAP_A64_SME_SFEXPA); GET_FEATURE_ID(aa64_fprcvt, ARM_HWCAP_A64_FPRCVT); GET_FEATURE_ID(aa64_sme_mop4, ARM_HWCAP_A64_SME_SMOP4); + GET_FEATURE_ID(aa64_sme_tmop, ARM_HWCAP_A64_SME_STMOP); return hwcaps; } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 8a3ebf6321..24f34947ec 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1384,8 +1384,9 @@ void aarch64_max_tcg_initfn(Object *obj) SET_IDREG(isar, ID_AA64DFR0, t); t = GET_IDREG(isar, ID_AA64SMFR0); - t = FIELD_DP64(t, ID_AA64SMFR0, SFEXPA, 1); /* FEAT_SSVE_FEXPA */ t = FIELD_DP64(t, ID_AA64SMFR0, SMOP4, 1); /* FEAT_SME_MOP4 */ + t = FIELD_DP64(t, ID_AA64SMFR0, STMOP, 1); /* FEAT_SME_TMOP */ + t = FIELD_DP64(t, ID_AA64SMFR0, SFEXPA, 1); /* FEAT_SSVE_FEXPA */ t = FIELD_DP64(t, ID_AA64SMFR0, AES, 1); /* FEAT_SSVE_AES */ t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP2, 1); /* FEAT_SSVE_FP8DOT2 */ t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP4, 1); /* FEAT_SSVE_FP8DOT4 */ From 33a71a68c6e137c80c9f6a7370d546404ed38513 Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 447/860] hw/arm: Add Axiado SoC AX3000 This patch adds new model for Axiado SoC AX3000 which supports 4 Cortex-A53 ARM64 CPUs Arm Generic Interrupt Controller v3 4 Cadence UARTs Signed-off-by: Kuan-Jui Chiu Message-id: 20260713073033.3883619-2-kchiu@axiado.com Reviewed-by: Peter Maydell [PMM: Kconfig for the SoC shouldn't depend on ARM] Signed-off-by: Peter Maydell --- MAINTAINERS | 7 ++ hw/arm/Kconfig | 6 ++ hw/arm/ax3000-soc.c | 182 ++++++++++++++++++++++++++++++++++++ hw/arm/meson.build | 3 + include/hw/arm/ax3000-soc.h | 68 ++++++++++++++ 5 files changed, 266 insertions(+) create mode 100644 hw/arm/ax3000-soc.c create mode 100644 include/hw/arm/ax3000-soc.h diff --git a/MAINTAINERS b/MAINTAINERS index 4628793dde..fce65e370f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1300,6 +1300,13 @@ M: Manos Pitsidianakis S: Maintained F: rust/hw/char/pl011/ +Axiado SoCs and EVKs +M: Kuan-Jui Chiu +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/ax3000*.c +F: include/hw/arm/ax3000*.h + AVR Machines ------------- diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 82e0bc2e70..2ed2c2c6e8 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -725,3 +725,9 @@ config ARMSSE select UNIMP select SSE_COUNTER select SSE_TIMER + +config AXIADO_SOC + bool + select ARM_GIC + select CADENCE # UART + select UNIMP diff --git a/hw/arm/ax3000-soc.c b/hw/arm/ax3000-soc.c new file mode 100644 index 0000000000..b528f8af53 --- /dev/null +++ b/hw/arm/ax3000-soc.c @@ -0,0 +1,182 @@ +/* + * Axiado SoC AX3000 + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/address-spaces.h" +#include "hw/arm/bsa.h" +#include "hw/arm/ax3000-soc.h" +#include "hw/misc/unimp.h" +#include "system/system.h" +#include "qobject/qlist.h" +#include "qom/object.h" +#include "hw/core/boards.h" + +static void ax3000_init(Object *obj) +{ + Ax3000SoCState *s = AX3000_SOC(obj); + Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s); + + for (int i = 0; i < sc->num_cpus; i++) { + g_autofree char *name = g_strdup_printf("cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], + ARM_CPU_TYPE_NAME("cortex-a53")); + } + + object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); + + for (int i = 0; i < AX3000_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i); + object_initialize_child(obj, name, &s->uart[i], TYPE_CADENCE_UART); + } +} + +static void ax3000_realize(DeviceState *dev, Error **errp) +{ + Ax3000SoCState *s = AX3000_SOC(dev); + Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s); + SysBusDevice *gic_sbd = SYS_BUS_DEVICE(&s->gic); + DeviceState *gic_dev = DEVICE(&s->gic); + QList *redist_region_count; + SysBusDevice *sdhci0_sbd; + DeviceState *card; + + /* CPUs */ + for (int i = 0; i < sc->num_cpus; i++) { + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, + &error_abort); + + if (object_property_find(OBJECT(&s->cpu[i]), "has_el3")) { + object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", + false, &error_abort); + } + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + qdev_prop_set_uint32(gic_dev, "num-cpu", sc->num_cpus); + qdev_prop_set_uint32(gic_dev, "num-irq", + AX3000_NUM_IRQS + GIC_INTERNAL); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, sc->num_cpus); + qdev_prop_set_array(gic_dev, "redist-region-count", redist_region_count); + + if (!sysbus_realize(gic_sbd, errp)) { + return; + } + + sysbus_mmio_map(gic_sbd, 0, AX3000_GIC_DIST_BASE); + sysbus_mmio_map(gic_sbd, 1, AX3000_GIC_REDIST_BASE); + + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + const int timer_irqs[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ + }; + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, and + * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs. + */ + for (int i = 0; i < sc->num_cpus; i++) { + DeviceState *cpu_dev = DEVICE(&s->cpu[i]); + int intidbase = AX3000_NUM_IRQS + i * GIC_INTERNAL; + qemu_irq irq; + + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { + irq = qdev_get_gpio_in(gic_dev, intidbase + timer_irqs[j]); + qdev_connect_gpio_out(cpu_dev, j, irq); + } + + irq = qdev_get_gpio_in(gic_dev, intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpu_dev, "gicv3-maintenance-interrupt", + 0, irq); + + sysbus_connect_irq(gic_sbd, i, + qdev_get_gpio_in(cpu_dev, ARM_CPU_IRQ)); + sysbus_connect_irq(gic_sbd, i + sc->num_cpus, + qdev_get_gpio_in(cpu_dev, ARM_CPU_FIQ)); + sysbus_connect_irq(gic_sbd, i + 2 * sc->num_cpus, + qdev_get_gpio_in(cpu_dev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gic_sbd, i + 3 * sc->num_cpus, + qdev_get_gpio_in(cpu_dev, ARM_CPU_VFIQ)); + } + + /* DRAM */ + const struct { + hwaddr addr; + size_t size; + const char *name; + } dram_table[] = { + { AX3000_DRAM0_BASE, AX3000_DRAM0_SIZE, "dram0" }, + { AX3000_DRAM1_BASE, AX3000_DRAM1_SIZE, "dram1" } + }; + + for (int i = 0; i < AX3000_NUM_BANKS; i++) { + memory_region_init_ram(&s->dram[i], OBJECT(s), dram_table[i].name, + dram_table[i].size, &error_fatal); + memory_region_add_subregion(get_system_memory(), dram_table[i].addr, + &s->dram[i]); + } + + /* UARTs */ + const struct { + hwaddr addr; + unsigned int irq; + } serial_table[] = { + { AX3000_UART0_BASE, AX3000_UART0_IRQ }, + { AX3000_UART1_BASE, AX3000_UART1_IRQ }, + { AX3000_UART2_BASE, AX3000_UART2_IRQ }, + { AX3000_UART3_BASE, AX3000_UART3_IRQ } + }; + + for (int i = 0; i < AX3000_NUM_UARTS; i++) { + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(gic_dev, serial_table[i].irq)); + } + + /* Timer control */ + create_unimplemented_device("ax3000.timerctrl", AX3000_TIMER_CTRL, 32); +} + +static void ax3000_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + Ax3000SoCClass *sc = AX3000_SOC_CLASS(oc); + + dc->desc = "Axiado SoC AX3000"; + dc->realize = ax3000_realize; + sc->num_cpus = AX3000_NUM_CPUS; +} + +static const TypeInfo axiado_soc_types[] = { + { + .name = TYPE_AX3000_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Ax3000SoCState), + .instance_init = ax3000_init, + .class_init = ax3000_class_init, + } +}; + +DEFINE_TYPES(axiado_soc_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 4233a800be..0254b82df1 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -110,6 +110,9 @@ arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) +arm_common_ss.add(when: ['CONFIG_AXIADO_SOC', 'TARGET_AARCH64'], if_true: files( + 'ax3000-soc.c')) + arm_common_ss.add(files('boot.c')) hw_common_arch += {'arm': arm_common_ss} diff --git a/include/hw/arm/ax3000-soc.h b/include/hw/arm/ax3000-soc.h new file mode 100644 index 0000000000..b984d7a8ec --- /dev/null +++ b/include/hw/arm/ax3000-soc.h @@ -0,0 +1,68 @@ +/* + * Axiado SoC AX3000 + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AXIADO_AX3000_H +#define AXIADO_AX3000_H + +#include "cpu.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/char/cadence_uart.h" +#include "hw/core/sysbus.h" +#include "qemu/units.h" + +#define TYPE_AX3000_SOC "ax3000" +OBJECT_DECLARE_TYPE(Ax3000SoCState, Ax3000SoCClass, AX3000_SOC) + +#define AX3000_DRAM0_BASE 0x3C000000 +#define AX3000_DRAM0_SIZE (1088 * MiB) +#define AX3000_DRAM1_BASE 0x400000000 +#define AX3000_DRAM1_SIZE (2 * GiB) + +#define AX3000_GIC_DIST_BASE 0x80300000 +#define AX3000_GIC_DIST_SIZE (64 * KiB) +#define AX3000_GIC_REDIST_BASE 0x80380000 +#define AX3000_GIC_REDIST_SIZE (512 * KiB) + +#define AX3000_UART0_BASE 0x80520000 +#define AX3000_UART1_BASE 0x805a0000 +#define AX3000_UART2_BASE 0x80620000 +#define AX3000_UART3_BASE 0x80520800 + +#define AX3000_TIMER_CTRL 0x8A020000 +#define AX3000_PLL_BASE 0x80000000 + +enum Ax3000Configuration { + AX3000_NUM_CPUS = 4, + AX3000_NUM_IRQS = 224, + AX3000_NUM_BANKS = 2, + AX3000_NUM_UARTS = 4, +}; + +typedef struct Ax3000SoCState { + SysBusDevice parent; + + ARMCPU cpu[AX3000_NUM_CPUS]; + GICv3State gic; + MemoryRegion dram[AX3000_NUM_BANKS]; + CadenceUARTState uart[AX3000_NUM_UARTS]; +} Ax3000SoCState; + +typedef struct Ax3000SoCClass { + SysBusDeviceClass parent; + + uint32_t num_cpus; +} Ax3000SoCClass; + +enum Ax3000Irqs { + AX3000_UART0_IRQ = 112, + AX3000_UART1_IRQ = 113, + AX3000_UART2_IRQ = 114, + AX3000_UART3_IRQ = 170, +}; + +#endif /* AXIADO_AX3000_H */ From 38b52563758ec3fe5df2ba8efa86b155ad79649d Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 448/860] hw/misc: Add AX3000 clock control This patch adds a new model for Axiado AX3000 clock control which supports to read ID and status Signed-off-by: Kuan-Jui Chiu Message-id: 20260713073033.3883619-3-kchiu@axiado.com [PMM: use HWADDR_PRIx] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/Kconfig | 3 ++ hw/misc/axiado_clk.c | 79 ++++++++++++++++++++++++++++++++++++ hw/misc/meson.build | 3 ++ include/hw/misc/axiado_clk.h | 26 ++++++++++++ 4 files changed, 111 insertions(+) create mode 100644 hw/misc/axiado_clk.c create mode 100644 include/hw/misc/axiado_clk.h diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 1543ee6653..b8860dd3e7 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -257,4 +257,7 @@ config XLNX_VERSAL_TRNG config XLNX_ZYNQ_DDRC bool +config AXIADO_CLK + bool + source macio/Kconfig diff --git a/hw/misc/axiado_clk.c b/hw/misc/axiado_clk.c new file mode 100644 index 0000000000..090beb38b5 --- /dev/null +++ b/hw/misc/axiado_clk.c @@ -0,0 +1,79 @@ +/* + * Axiado Clock Control + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/misc/axiado_clk.h" +#include "qemu/log.h" + +#define CLKRST_CPU_PLL_POSTDIV_OFFSET 0x0C +#define CLKRST_CPU_PLL_STS_OFFSET 0x14 + +static uint64_t pll_read(void *opaque, hwaddr offset, unsigned size) +{ + switch (offset) { + case CLKRST_CPU_PLL_POSTDIV_OFFSET: + return 0x20891b; + case CLKRST_CPU_PLL_STS_OFFSET: + return 0x01; + default: + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); + break; + } + return 0x00; +} + +static void pll_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); +} + +static const MemoryRegionOps pll_ops = { + .read = pll_read, + .write = pll_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void ax3000_clk_init(Object *obj) +{ + Ax3000ClkState *s = AX3000_CLK(obj); + + memory_region_init_io(&s->pll_ctrl, obj, &pll_ops, s, + TYPE_AX3000_CLK, AX3000_CLK_PLL_CTRL_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->pll_ctrl); +} + +static void ax3000_clk_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "Axiado AX3000 Clock Control"; +} + +static const TypeInfo ax3000_clk_info = { + .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_AX3000_CLK, + .instance_size = sizeof(Ax3000ClkState), + .instance_init = ax3000_clk_init, + .class_init = ax3000_clk_class_init, +}; + +static void axiado_clk_register_type(void) +{ + type_register_static(&ax3000_clk_info); +} +type_init(axiado_clk_register_type); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 23265f6035..e86d9ad6b3 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -1,4 +1,5 @@ system_ss.add(when: 'CONFIG_APPLESMC', if_true: files('applesmc.c')) + system_ss.add(when: 'CONFIG_EDU', if_true: files('edu.c')) system_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmcoreinfo.c')) system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugexit.c')) @@ -168,3 +169,5 @@ system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) # HPPA devices system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c')) + +system_ss.add(when: 'CONFIG_AXIADO_CLK', if_true: files('axiado_clk.c')) diff --git a/include/hw/misc/axiado_clk.h b/include/hw/misc/axiado_clk.h new file mode 100644 index 0000000000..6e12a509d7 --- /dev/null +++ b/include/hw/misc/axiado_clk.h @@ -0,0 +1,26 @@ +/* + * Axiado AX3000 Clock Control + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AXIADO_AX3000_CLK_H +#define AXIADO_AX3000_CLK_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" + +#define TYPE_AX3000_CLK "ax3000-clk" +OBJECT_DECLARE_SIMPLE_TYPE(Ax3000ClkState, AX3000_CLK) + +#define AX3000_CLK_PLL_CTRL_SIZE 0x1000 + +typedef struct Ax3000ClkState { + SysBusDevice parent; + + MemoryRegion pll_ctrl; +} Ax3000ClkState; + +#endif /* AXIADO_AX3000_CLK_H */ From 406738e50861b2deb19414a73b8a9079565523d8 Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 449/860] hw/arm: ax3000-soc: Enable Ax3000 clock control Signed-off-by: Kuan-Jui Chiu Message-id: 20260713073033.3883619-4-kchiu@axiado.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/ax3000-soc.c | 8 ++++++++ include/hw/arm/ax3000-soc.h | 2 ++ 3 files changed, 11 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 2ed2c2c6e8..69161a439c 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -730,4 +730,5 @@ config AXIADO_SOC bool select ARM_GIC select CADENCE # UART + select AXIADO_CLK select UNIMP diff --git a/hw/arm/ax3000-soc.c b/hw/arm/ax3000-soc.c index b528f8af53..aaeae11074 100644 --- a/hw/arm/ax3000-soc.c +++ b/hw/arm/ax3000-soc.c @@ -33,6 +33,8 @@ static void ax3000_init(Object *obj) g_autofree char *name = g_strdup_printf("uart%d", i); object_initialize_child(obj, name, &s->uart[i], TYPE_CADENCE_UART); } + + object_initialize_child(obj, "clk", &s->ax3000_clk, TYPE_AX3000_CLK); } static void ax3000_realize(DeviceState *dev, Error **errp) @@ -157,6 +159,12 @@ static void ax3000_realize(DeviceState *dev, Error **errp) /* Timer control */ create_unimplemented_device("ax3000.timerctrl", AX3000_TIMER_CTRL, 32); + + /* Clock control */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ax3000_clk), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ax3000_clk), 0, AX3000_PLL_BASE); } static void ax3000_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/ax3000-soc.h b/include/hw/arm/ax3000-soc.h index b984d7a8ec..329aa4e769 100644 --- a/include/hw/arm/ax3000-soc.h +++ b/include/hw/arm/ax3000-soc.h @@ -12,6 +12,7 @@ #include "cpu.h" #include "hw/intc/arm_gicv3_common.h" #include "hw/char/cadence_uart.h" +#include "hw/misc/axiado_clk.h" #include "hw/core/sysbus.h" #include "qemu/units.h" @@ -49,6 +50,7 @@ typedef struct Ax3000SoCState { ARMCPU cpu[AX3000_NUM_CPUS]; GICv3State gic; MemoryRegion dram[AX3000_NUM_BANKS]; + Ax3000ClkState ax3000_clk; CadenceUARTState uart[AX3000_NUM_UARTS]; } Ax3000SoCState; From 8c55a630c5b91fd7292fb11ee0e69a5914b66487 Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 450/860] hw/sd: Add Axiado SD host controller with eMMC PHY This patch add a new model for Axiado SD host controller which is compatible with SDHCI 3.0 spec This device model also includes a eMMC PHY which helps to control SD/eMMC Signed-off-by: Kuan-Jui Chiu Reviewed-by: Peter Maydell Message-id: 20260713073033.3883619-5-kchiu@axiado.com [PMM: Use HWADDR_PRIx] Signed-off-by: Peter Maydell --- MAINTAINERS | 2 + hw/sd/Kconfig | 4 ++ hw/sd/axiado_sdhci.c | 119 +++++++++++++++++++++++++++++++++++ hw/sd/meson.build | 1 + include/hw/sd/axiado_sdhci.h | 21 +++++++ 5 files changed, 147 insertions(+) create mode 100644 hw/sd/axiado_sdhci.c create mode 100644 include/hw/sd/axiado_sdhci.h diff --git a/MAINTAINERS b/MAINTAINERS index fce65e370f..7a02fed0d6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1305,7 +1305,9 @@ M: Kuan-Jui Chiu L: qemu-arm@nongnu.org S: Maintained F: hw/arm/ax3000*.c +F: hw/*/axiado*.c F: include/hw/arm/ax3000*.h +F: include/hw/*/axiado*.h AVR Machines ------------- diff --git a/hw/sd/Kconfig b/hw/sd/Kconfig index 633b9afec9..c69bf24f8d 100644 --- a/hw/sd/Kconfig +++ b/hw/sd/Kconfig @@ -23,3 +23,7 @@ config SDHCI_PCI config CADENCE_SDHCI bool select SDHCI + +config AXIADO_SDHCI + bool + select SDHCI diff --git a/hw/sd/axiado_sdhci.c b/hw/sd/axiado_sdhci.c new file mode 100644 index 0000000000..2e88bffdd1 --- /dev/null +++ b/hw/sd/axiado_sdhci.c @@ -0,0 +1,119 @@ +/* + * Axiado SD Host Controller with embedded PHY + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/sd/axiado_sdhci.h" +#include "sdhci-internal.h" +#include "qapi/error.h" +#include "hw/core/qdev-properties.h" +#include "qemu/log.h" + +#define EMMC_PHY_ID 0x00 +#define EMMC_PHY_STATUS 0x50 + +#define DLL_RDY (1u << 0) +#define CAL_DONE (1u << 6) + +static uint64_t emmc_phy_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t val = 0x00; + + switch (offset) { + case EMMC_PHY_ID: + val = 0x3dff6870; + break; + case EMMC_PHY_STATUS: + val = DLL_RDY | CAL_DONE; + break; + default: + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); + break; + } + + return val; +} + +static void emmc_phy_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + qemu_log_mask(LOG_UNIMP, + "Register 0x%" HWADDR_PRIx " not implemented\n", offset); +} + +static const MemoryRegionOps emmc_phy_ops = { + .read = emmc_phy_read, + .write = emmc_phy_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void axiado_sdhci_realize(DeviceState *dev, Error **errp) +{ + AxiadoSDHCIState *s = AXIADO_SDHCI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SysBusDevice *sdhci_sbd; + + qdev_prop_set_uint64(DEVICE(&s->sdhci), "capareg", 0x216737eed0b0); + qdev_prop_set_uint64(DEVICE(&s->sdhci), "sd-spec-version", 3); + + sdhci_sbd = SYS_BUS_DEVICE(&s->sdhci); + if (!sysbus_realize(sdhci_sbd, errp)) { + return; + } + + sysbus_init_mmio(sbd, sysbus_mmio_get_region(sdhci_sbd, 0)); + + /* Propagate IRQ from SDHCI and SD bus */ + sysbus_pass_irq(sbd, sdhci_sbd); + s->sd_bus = qdev_get_child_bus(DEVICE(sdhci_sbd), "sd-bus"); + + /* Initialize eMMC PHY MMIO */ + memory_region_init_io(&s->emmc_phy, OBJECT(s), &emmc_phy_ops, s, + "axiado.emmc-phy", 0x1000); + + sysbus_init_mmio(sbd, &s->emmc_phy); +} + +static void axiado_sdhci_instance_init(Object *obj) +{ + AxiadoSDHCIState *s = AXIADO_SDHCI(obj); + + object_initialize_child(OBJECT(s), "sdhci", &s->sdhci, + TYPE_SYSBUS_SDHCI); +} + +static void axiado_sdhci_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = axiado_sdhci_realize; + dc->desc = "Axiado SD Host Controller with eMMC PHY"; +} + +static const TypeInfo axiado_sdhci_info = { + .name = TYPE_AXIADO_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(AxiadoSDHCIState), + .instance_init = axiado_sdhci_instance_init, + .class_init = axiado_sdhci_class_init, +}; + +static void axiado_sdhci_register_types(void) +{ + type_register_static(&axiado_sdhci_info); +} + +type_init(axiado_sdhci_register_types); diff --git a/hw/sd/meson.build b/hw/sd/meson.build index b43d45bc56..ebf09e30a4 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -10,3 +10,4 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_sdhci.c')) system_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) +system_ss.add(when: 'CONFIG_AXIADO_SDHCI', if_true: files('axiado_sdhci.c')) diff --git a/include/hw/sd/axiado_sdhci.h b/include/hw/sd/axiado_sdhci.h new file mode 100644 index 0000000000..85afebad93 --- /dev/null +++ b/include/hw/sd/axiado_sdhci.h @@ -0,0 +1,21 @@ +/* + * Axiado SD Host Controller + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "hw/sd/sdhci.h" +#include "qom/object.h" + +#define TYPE_AXIADO_SDHCI "axiado-sdhci" +OBJECT_DECLARE_SIMPLE_TYPE(AxiadoSDHCIState, AXIADO_SDHCI) + +typedef struct AxiadoSDHCIState { + SysBusDevice parent; + + SDHCIState sdhci; + MemoryRegion emmc_phy; + BusState *sd_bus; +} AxiadoSDHCIState; From 2019f4e0ddb3f7c0d3246042ee7535b7348792ce Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 451/860] hw/arm: ax3000-soc: Enable Axiado SD host controller Enable SD host controller into Axiado AX3000 SoC to load kernel and rootfs from eMMC. Signed-off-by: Kuan-Jui Chiu Reviewed-by: Peter Maydell Message-id: 20260713073033.3883619-6-kchiu@axiado.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/ax3000-soc.c | 22 ++++++++++++++++++++++ include/hw/arm/ax3000-soc.h | 7 +++++++ 3 files changed, 30 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 69161a439c..32e7d83339 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -731,4 +731,5 @@ config AXIADO_SOC select ARM_GIC select CADENCE # UART select AXIADO_CLK + select AXIADO_SDHCI select UNIMP diff --git a/hw/arm/ax3000-soc.c b/hw/arm/ax3000-soc.c index aaeae11074..23903b081b 100644 --- a/hw/arm/ax3000-soc.c +++ b/hw/arm/ax3000-soc.c @@ -35,6 +35,7 @@ static void ax3000_init(Object *obj) } object_initialize_child(obj, "clk", &s->ax3000_clk, TYPE_AX3000_CLK); + object_initialize_child(obj, "sdhci0", &s->sdhci0, TYPE_AXIADO_SDHCI); } static void ax3000_realize(DeviceState *dev, Error **errp) @@ -46,6 +47,7 @@ static void ax3000_realize(DeviceState *dev, Error **errp) QList *redist_region_count; SysBusDevice *sdhci0_sbd; DeviceState *card; + DriveInfo *dinfo; /* CPUs */ for (int i = 0; i < sc->num_cpus; i++) { @@ -165,6 +167,26 @@ static void ax3000_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->ax3000_clk), 0, AX3000_PLL_BASE); + + /* SDHCI */ + sdhci0_sbd = SYS_BUS_DEVICE(&s->sdhci0); + if (!sysbus_realize(sdhci0_sbd, errp)) { + return; + } + + sysbus_mmio_map(sdhci0_sbd, 0, AX3000_SDHCI0_BASE); + sysbus_mmio_map(sdhci0_sbd, 1, AX3000_EMMC_PHY_BASE); + sysbus_connect_irq(sdhci0_sbd, 0, + qdev_get_gpio_in(gic_dev, AX3000_SDHCI0_IRQ)); + + dinfo = drive_get(IF_SD, 0, 0); + if (dinfo) { + card = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(card, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); + qdev_realize_and_unref(card, s->sdhci0.sd_bus, &error_fatal); + } } static void ax3000_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/ax3000-soc.h b/include/hw/arm/ax3000-soc.h index 329aa4e769..52b47aa829 100644 --- a/include/hw/arm/ax3000-soc.h +++ b/include/hw/arm/ax3000-soc.h @@ -13,6 +13,7 @@ #include "hw/intc/arm_gicv3_common.h" #include "hw/char/cadence_uart.h" #include "hw/misc/axiado_clk.h" +#include "hw/sd/axiado_sdhci.h" #include "hw/core/sysbus.h" #include "qemu/units.h" @@ -34,6 +35,9 @@ OBJECT_DECLARE_TYPE(Ax3000SoCState, Ax3000SoCClass, AX3000_SOC) #define AX3000_UART2_BASE 0x80620000 #define AX3000_UART3_BASE 0x80520800 +#define AX3000_SDHCI0_BASE 0x86000000 +#define AX3000_EMMC_PHY_BASE 0x80801C00 + #define AX3000_TIMER_CTRL 0x8A020000 #define AX3000_PLL_BASE 0x80000000 @@ -52,6 +56,7 @@ typedef struct Ax3000SoCState { MemoryRegion dram[AX3000_NUM_BANKS]; Ax3000ClkState ax3000_clk; CadenceUARTState uart[AX3000_NUM_UARTS]; + AxiadoSDHCIState sdhci0; } Ax3000SoCState; typedef struct Ax3000SoCClass { @@ -65,6 +70,8 @@ enum Ax3000Irqs { AX3000_UART1_IRQ = 113, AX3000_UART2_IRQ = 114, AX3000_UART3_IRQ = 170, + + AX3000_SDHCI0_IRQ = 123, }; #endif /* AXIADO_AX3000_H */ From 4e5205339fc4b5e56ed4d97d4a504bdee779fb8f Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 452/860] hw/arm: Add Axiado EVK SCM3003 Add EVK axiado-scm3003 built with AX3000 SoC Signed-off-by: Kuan-Jui Chiu Message-id: 20260713073033.3883619-7-kchiu@axiado.com Reviewed-by: Peter Maydell [PMM: KConfig for the board has to depend on TCG && ARM] Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 6 ++++ hw/arm/ax3000-boards.c | 56 ++++++++++++++++++++++++++++++++++ hw/arm/ax3000-evk.c | 27 ++++++++++++++++ hw/arm/meson.build | 3 ++ include/hw/arm/ax3000-boards.h | 28 +++++++++++++++++ 5 files changed, 120 insertions(+) create mode 100644 hw/arm/ax3000-boards.c create mode 100644 hw/arm/ax3000-evk.c create mode 100644 include/hw/arm/ax3000-boards.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 32e7d83339..d16b3d2f47 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -733,3 +733,9 @@ config AXIADO_SOC select AXIADO_CLK select AXIADO_SDHCI select UNIMP + +config AXIADO_EVK + bool + default y + depends on TCG && ARM + select AXIADO_SOC diff --git a/hw/arm/ax3000-boards.c b/hw/arm/ax3000-boards.c new file mode 100644 index 0000000000..4b8deec15c --- /dev/null +++ b/hw/arm/ax3000-boards.c @@ -0,0 +1,56 @@ +/* + * Axiado Boards + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/arm/ax3000-boards.h" +#include "hw/arm/boot.h" +#include "hw/arm/machines-qom.h" +#include "qemu/error-report.h" +#include "qom/object.h" + +static struct arm_boot_info ax3000_binfo = { + .loader_start = AX3000_DRAM0_BASE, + .board_id = -1, +}; + +static void ax3000_machine_init(MachineState *machine) +{ + Ax3000MachineState *ams = AX3000_MACHINE(machine); + + ams->soc = AX3000_SOC(object_new(TYPE_AX3000_SOC)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(ams->soc)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(ams->soc), &error_fatal); + + ax3000_binfo.ram_size = machine->ram_size; + arm_load_kernel(&ams->soc->cpu[0], machine, &ax3000_binfo); +} + +static void ax3000_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = ax3000_machine_init; + mc->default_cpus = AX3000_NUM_CPUS; + mc->min_cpus = AX3000_NUM_CPUS; + mc->max_cpus = AX3000_NUM_CPUS; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); +} + +static const TypeInfo ax3000_machine_types[] = { + { + .name = TYPE_AX3000_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(Ax3000MachineState), + .class_size = sizeof(Ax3000MachineClass), + .class_init = ax3000_machine_class_init, + .interfaces = aarch64_machine_interfaces, + .abstract = true, + } +}; + +DEFINE_TYPES(ax3000_machine_types) diff --git a/hw/arm/ax3000-evk.c b/hw/arm/ax3000-evk.c new file mode 100644 index 0000000000..a170848871 --- /dev/null +++ b/hw/arm/ax3000-evk.c @@ -0,0 +1,27 @@ +/* + * Axiado Evaluation Kit Emulation + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/arm/ax3000-boards.h" + +static void axiado_scm3003_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Axiado SCM3003 EVK Board"; +} + +static const TypeInfo ax3000_evk_types[] = { + { + .name = MACHINE_TYPE_NAME("axiado-scm3003"), + .parent = TYPE_AX3000_MACHINE, + .class_init = axiado_scm3003_class_init, + } +}; + +DEFINE_TYPES(ax3000_evk_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 0254b82df1..8ee5307a91 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -112,6 +112,9 @@ arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) arm_common_ss.add(when: ['CONFIG_AXIADO_SOC', 'TARGET_AARCH64'], if_true: files( 'ax3000-soc.c')) +arm_common_ss.add(when: ['CONFIG_AXIADO_EVK', 'TARGET_AARCH64'], if_true: files( + 'ax3000-boards.c', + 'ax3000-evk.c')) arm_common_ss.add(files('boot.c')) diff --git a/include/hw/arm/ax3000-boards.h b/include/hw/arm/ax3000-boards.h new file mode 100644 index 0000000000..4a632661e1 --- /dev/null +++ b/include/hw/arm/ax3000-boards.h @@ -0,0 +1,28 @@ +/* + * Axiado Boards + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef AXIADO_BOARD_H +#define AXIADO_BOARD_H + +#include "hw/core/boards.h" +#include "hw/arm/ax3000-soc.h" + +#define TYPE_AX3000_MACHINE MACHINE_TYPE_NAME("ax3000") +OBJECT_DECLARE_TYPE(Ax3000MachineState, Ax3000MachineClass, AX3000_MACHINE) + +typedef struct Ax3000MachineState { + MachineState parent; + + Ax3000SoCState *soc; +} Ax3000MachineState; + +typedef struct Ax3000MachineClass { + MachineClass parent; + +} Ax3000MachineClass; +#endif From cc57a945feb0906af6b56591c790f60bfd65945c Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 453/860] hw/gpio: Add Cadence GPIO controller This patch add a new model for Cadence GPIO controller which supports 32 pins and interrupts for level-triggered/edge-triggered type on input pins. Also define new trace functions for analysis purpose and new configuration to enable this model. Signed-off-by: Kuan-Jui Chiu Message-id: 20260713073033.3883619-8-kchiu@axiado.com [PMM: drop unnecessary and marker comments] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/gpio/Kconfig | 3 + hw/gpio/cadence_gpio.c | 292 +++++++++++++++++++++++++++++++++ hw/gpio/meson.build | 1 + hw/gpio/trace-events | 5 + include/hw/gpio/cadence_gpio.h | 53 ++++++ 5 files changed, 354 insertions(+) create mode 100644 hw/gpio/cadence_gpio.c create mode 100644 include/hw/gpio/cadence_gpio.h diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig index a209294c20..fcc7c70bd5 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -30,3 +30,6 @@ config PCF8574 config ZAURUS_SCOOP bool + +config CADENCE_GPIO + bool diff --git a/hw/gpio/cadence_gpio.c b/hw/gpio/cadence_gpio.c new file mode 100644 index 0000000000..ce8256b381 --- /dev/null +++ b/hw/gpio/cadence_gpio.c @@ -0,0 +1,292 @@ +/* + * Cadence GPIO emulation. + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/gpio/cadence_gpio.h" +#include "hw/core/irq.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "trace.h" + +static void cdns_gpio_update_irq(CadenceGPIOState *s) +{ + qemu_set_irq(s->irq, s->isr ? 1 : 0); +} + +static void cdns_gpio_update_isr_with_inpvr(CadenceGPIOState *s, uint32_t new) +{ + uint32_t new_isr = 0; + uint32_t any_edges, rising_edges, falling_edges, deassert_mask; + + /* + * If ITR is set, this is level triggered: + * set corresponding ISR bits when IVR matches new inpvr value. + */ + new_isr |= s->itr & ~(s->ivr ^ new); + + /* + * If ITR is not set, this is edge-triggered: + * If IOAR bit is set, trigger on any edge; + * otherwise trigger on rising edge if IVR is set, + * trigger on falling edge if IVR bit is 0. + */ + any_edges = s->ioar & (s->inpvr ^ new); + rising_edges = s->ivr & ~s->inpvr & new; + falling_edges = ~s->ivr & s->inpvr & ~new; + new_isr |= ~s->itr & (any_edges | rising_edges | falling_edges); + + /* + * In bypass mode or if this isn't an input pin, the corresponding ISR + * bit is forced to zero. + */ + deassert_mask = s->bmr | ~s->dmr | s->imr; + + new_isr &= ~deassert_mask; + s->isr = new_isr; + + cdns_gpio_update_irq(s); +} + +static void cdns_gpio_update_isr(CadenceGPIOState *s) +{ + cdns_gpio_update_isr_with_inpvr(s, s->inpvr); +} + +static void cdns_gpio_set(void *opaque, int line, int level) +{ + CadenceGPIOState *s = CADENCE_GPIO(opaque); + uint32_t new_inpvr = deposit32(s->inpvr, line, 1, level ? 1 : 0); + + trace_cdns_gpio_set(DEVICE(s)->canonical_path, line, level); + + cdns_gpio_update_isr_with_inpvr(s, new_inpvr); + + /* Sync INPVR with new value */ + s->inpvr = new_inpvr; +} + +static inline void cdns_gpio_update_output_irq(CadenceGPIOState *s) +{ + uint32_t is_output = ~s->bmr & ~s->dmr & s->oer; + + for (int i = 0; i < CDNS_GPIO_NUM; i++) { + if (extract32(is_output, i, 1)) { + /* Forward the output value to corresponding irq */ + qemu_set_irq(s->output[i], extract32(s->ovr, i, 1)); + } + } +} + +static uint64_t cdns_gpio_read(void *opaque, hwaddr offset, unsigned size) +{ + CadenceGPIOState *s = CADENCE_GPIO(opaque); + uint32_t reg_value = 0x0; + + switch (offset) { + case CDNS_GPIO_BYPASS_MODE: + reg_value = s->bmr; + break; + + case CDNS_GPIO_DIRECTION_MODE: + reg_value = s->dmr; + break; + + case CDNS_GPIO_OUTPUT_EN: + reg_value = s->oer; + break; + + case CDNS_GPIO_OUTPUT_VALUE: + reg_value = s->ovr; + break; + + case CDNS_GPIO_INPUT_VALUE: + reg_value = s->inpvr; + break; + + case CDNS_GPIO_IRQ_MASK: + reg_value = s->imr; + break; + + case CDNS_GPIO_IRQ_STATUS: + reg_value = s->isr; + break; + + case CDNS_GPIO_IRQ_TYPE: + reg_value = s->itr; + break; + + case CDNS_GPIO_IRQ_VALUE: + reg_value = s->ivr; + break; + + case CDNS_GPIO_IRQ_ANY_EDGE: + reg_value = s->ioar; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_CADENCE_GPIO, __func__, offset); + break; + } + + trace_cdns_gpio_read(DEVICE(s)->canonical_path, offset, reg_value); + + return reg_value; +} + +static void cdns_gpio_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + CadenceGPIOState *s = CADENCE_GPIO(opaque); + + trace_cdns_gpio_write(DEVICE(s)->canonical_path, offset, value); + + switch (offset) { + case CDNS_GPIO_BYPASS_MODE: + s->bmr = value; + cdns_gpio_update_output_irq(s); + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_DIRECTION_MODE: + s->dmr = value; + cdns_gpio_update_output_irq(s); + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_OUTPUT_EN: + s->oer = value; + cdns_gpio_update_output_irq(s); + break; + + case CDNS_GPIO_OUTPUT_VALUE: + s->ovr = value; + cdns_gpio_update_output_irq(s); + break; + + case CDNS_GPIO_IRQ_EN: + s->imr &= ~value; + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_IRQ_DIS: + s->imr |= value; + cdns_gpio_update_isr(s); + break; + + case CDNS_GPIO_IRQ_TYPE: + s->itr = value; + break; + + case CDNS_GPIO_IRQ_VALUE: + s->ivr = value; + break; + + case CDNS_GPIO_IRQ_ANY_EDGE: + s->ioar = value; + break; + + case CDNS_GPIO_INPUT_VALUE: + case CDNS_GPIO_IRQ_MASK: + case CDNS_GPIO_IRQ_STATUS: + /* Read-Only */ + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" + HWADDR_PRIx "\n", TYPE_CADENCE_GPIO, __func__, offset); + break; + } +} + +static const MemoryRegionOps cdns_gpio_ops = { + .read = cdns_gpio_read, + .write = cdns_gpio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const VMStateDescription vmstate_cdns_gpio = { + .name = TYPE_CADENCE_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(bmr, CadenceGPIOState), + VMSTATE_UINT32(dmr, CadenceGPIOState), + VMSTATE_UINT32(oer, CadenceGPIOState), + VMSTATE_UINT32(ovr, CadenceGPIOState), + VMSTATE_UINT32(inpvr, CadenceGPIOState), + VMSTATE_UINT32(imr, CadenceGPIOState), + VMSTATE_UINT32(isr, CadenceGPIOState), + VMSTATE_UINT32(itr, CadenceGPIOState), + VMSTATE_UINT32(ivr, CadenceGPIOState), + VMSTATE_UINT32(ioar, CadenceGPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static void cdns_gpio_reset(DeviceState *dev) +{ + CadenceGPIOState *s = CADENCE_GPIO(dev); + + s->bmr = 0; + s->dmr = 0; + s->oer = 0; + s->ovr = 0; + s->inpvr = 0; + s->imr = 0xffffffff; + s->isr = 0; + s->itr = 0; + s->ivr = 0; + s->ioar = 0; +} + +static void cdns_gpio_init(Object *obj) +{ + CadenceGPIOState *s = CADENCE_GPIO(obj); + + memory_region_init_io(&s->iomem, obj, &cdns_gpio_ops, s, + TYPE_CADENCE_GPIO, CDNS_GPIO_REG_SIZE); + + qdev_init_gpio_in(DEVICE(s), cdns_gpio_set, CDNS_GPIO_NUM); + qdev_init_gpio_out(DEVICE(s), s->output, CDNS_GPIO_NUM); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); +} + +static void cdns_gpio_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, cdns_gpio_reset); + dc->vmsd = &vmstate_cdns_gpio; + dc->desc = "Cadence GPIO controller"; +} + +static const TypeInfo cdns_gpio_info = { + .name = TYPE_CADENCE_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CadenceGPIOState), + .instance_init = cdns_gpio_init, + .class_init = cdns_gpio_class_init, +}; + +static void cdns_gpio_register_types(void) +{ + type_register_static(&cdns_gpio_info); +} + +type_init(cdns_gpio_register_types) diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index 6a67ee958f..0555f44b6a 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -19,3 +19,4 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sgpio.c')) system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) system_ss.add(when: 'CONFIG_PCF8574', if_true: files('pcf8574.c')) +system_ss.add(when: 'CONFIG_CADENCE_GPIO', if_true: files('cadence_gpio.c')) diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index cea896b28f..80ca783a03 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -46,3 +46,8 @@ stm32l4x5_gpio_read(char *gpio, uint64_t addr) "GPIO%s addr: 0x%" PRIx64 " " stm32l4x5_gpio_write(char *gpio, uint64_t addr, uint64_t data) "GPIO%s addr: 0x%" PRIx64 " val: 0x%" PRIx64 "" stm32l4x5_gpio_update_idr(char *gpio, uint32_t old_idr, uint32_t new_idr) "GPIO%s from: 0x%x to: 0x%x" stm32l4x5_gpio_pins(char *gpio, uint16_t disconnected, uint16_t high) "GPIO%s disconnected pins: 0x%x levels: 0x%x" + +# cadence_gpio.c +cdns_gpio_read(const char *path, uint64_t offset, uint32_t value) "%s:reg[0x%04" PRIx64 "] -> 0x%" PRIx32 +cdns_gpio_write(const char *path, uint64_t offset, uint64_t value) "%s:reg[0x%04" PRIx64 "] <- 0x%04" PRIx64 +cdns_gpio_set(const char *path, int line, int level) "%s:[%d] <- %d" diff --git a/include/hw/gpio/cadence_gpio.h b/include/hw/gpio/cadence_gpio.h new file mode 100644 index 0000000000..69646ba639 --- /dev/null +++ b/include/hw/gpio/cadence_gpio.h @@ -0,0 +1,53 @@ +/* + * Cadence GPIO registers definition. + * + * Author: Kuan-Jui Chiu + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef CADENCE_GPIO_H +#define CADENCE_GPIO_H + +#include "hw/core/sysbus.h" +#include "qom/object.h" + +#define TYPE_CADENCE_GPIO "cadence_gpio" +OBJECT_DECLARE_SIMPLE_TYPE(CadenceGPIOState, CADENCE_GPIO) + +#define CDNS_GPIO_REG_SIZE 0x400 +#define CDNS_GPIO_NUM 32 + +#define CDNS_GPIO_BYPASS_MODE 0x00 +#define CDNS_GPIO_DIRECTION_MODE 0x04 +#define CDNS_GPIO_OUTPUT_EN 0x08 +#define CDNS_GPIO_OUTPUT_VALUE 0x0c +#define CDNS_GPIO_INPUT_VALUE 0x10 +#define CDNS_GPIO_IRQ_MASK 0x14 +#define CDNS_GPIO_IRQ_EN 0x18 +#define CDNS_GPIO_IRQ_DIS 0x1c +#define CDNS_GPIO_IRQ_STATUS 0x20 +#define CDNS_GPIO_IRQ_TYPE 0x24 +#define CDNS_GPIO_IRQ_VALUE 0x28 +#define CDNS_GPIO_IRQ_ANY_EDGE 0x2c + +struct CadenceGPIOState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + + uint32_t bmr; + uint32_t dmr; + uint32_t oer; + uint32_t ovr; + uint32_t inpvr; + uint32_t imr; + uint32_t isr; + uint32_t itr; + uint32_t ivr; + uint32_t ioar; + qemu_irq irq; + qemu_irq output[CDNS_GPIO_NUM]; +}; + +#endif /* CADENCE_GPIO_H */ From a4256760c0e0a123569be2e99d8749f00b812c1d Mon Sep 17 00:00:00 2001 From: Kuan-Jui Chiu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 454/860] hw/arm: ax3000-soc: Enable Cadence GPIO controllers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable 8 Cadence GPIO controllers into Axiado AX3000 SoC Signed-off-by: Kuan-Jui Chiu Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260713073033.3883619-9-kchiu@axiado.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/arm/ax3000-soc.c | 30 ++++++++++++++++++++++++++++++ include/hw/arm/ax3000-soc.h | 21 +++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index d16b3d2f47..e22c34a2e1 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -731,6 +731,7 @@ config AXIADO_SOC select ARM_GIC select CADENCE # UART select AXIADO_CLK + select CADENCE_GPIO select AXIADO_SDHCI select UNIMP diff --git a/hw/arm/ax3000-soc.c b/hw/arm/ax3000-soc.c index 23903b081b..71e31c6fb4 100644 --- a/hw/arm/ax3000-soc.c +++ b/hw/arm/ax3000-soc.c @@ -36,6 +36,11 @@ static void ax3000_init(Object *obj) object_initialize_child(obj, "clk", &s->ax3000_clk, TYPE_AX3000_CLK); object_initialize_child(obj, "sdhci0", &s->sdhci0, TYPE_AXIADO_SDHCI); + + for (int i = 0; i < AX3000_NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%d", i); + object_initialize_child(obj, name, &s->gpio[i], TYPE_CADENCE_GPIO); + } } static void ax3000_realize(DeviceState *dev, Error **errp) @@ -187,6 +192,31 @@ static void ax3000_realize(DeviceState *dev, Error **errp) &error_fatal); qdev_realize_and_unref(card, s->sdhci0.sd_bus, &error_fatal); } + + /* GPIOs */ + const struct { + hwaddr addr; + unsigned int irq; + } gpio_table[] = { + { AX3000_GPIO0_BASE, AX3000_GPIO0_IRQ }, + { AX3000_GPIO1_BASE, AX3000_GPIO1_IRQ }, + { AX3000_GPIO2_BASE, AX3000_GPIO2_IRQ }, + { AX3000_GPIO3_BASE, AX3000_GPIO3_IRQ }, + { AX3000_GPIO4_BASE, AX3000_GPIO4_IRQ }, + { AX3000_GPIO5_BASE, AX3000_GPIO5_IRQ }, + { AX3000_GPIO6_BASE, AX3000_GPIO6_IRQ }, + { AX3000_GPIO7_BASE, AX3000_GPIO7_IRQ } + }; + + for (int i = 0; i < AX3000_NUM_GPIOS; i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(gic_dev, gpio_table[i].irq)); + } } static void ax3000_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/ax3000-soc.h b/include/hw/arm/ax3000-soc.h index 52b47aa829..344fcb6f3c 100644 --- a/include/hw/arm/ax3000-soc.h +++ b/include/hw/arm/ax3000-soc.h @@ -13,6 +13,7 @@ #include "hw/intc/arm_gicv3_common.h" #include "hw/char/cadence_uart.h" #include "hw/misc/axiado_clk.h" +#include "hw/gpio/cadence_gpio.h" #include "hw/sd/axiado_sdhci.h" #include "hw/core/sysbus.h" #include "qemu/units.h" @@ -38,6 +39,15 @@ OBJECT_DECLARE_TYPE(Ax3000SoCState, Ax3000SoCClass, AX3000_SOC) #define AX3000_SDHCI0_BASE 0x86000000 #define AX3000_EMMC_PHY_BASE 0x80801C00 +#define AX3000_GPIO0_BASE 0x80500000 +#define AX3000_GPIO1_BASE 0x80580000 +#define AX3000_GPIO2_BASE 0x80600000 +#define AX3000_GPIO3_BASE 0x80680000 +#define AX3000_GPIO4_BASE 0x80700000 +#define AX3000_GPIO5_BASE 0x80780000 +#define AX3000_GPIO6_BASE 0x80800000 +#define AX3000_GPIO7_BASE 0x80880000 + #define AX3000_TIMER_CTRL 0x8A020000 #define AX3000_PLL_BASE 0x80000000 @@ -46,6 +56,7 @@ enum Ax3000Configuration { AX3000_NUM_IRQS = 224, AX3000_NUM_BANKS = 2, AX3000_NUM_UARTS = 4, + AX3000_NUM_GPIOS = 8, }; typedef struct Ax3000SoCState { @@ -56,6 +67,7 @@ typedef struct Ax3000SoCState { MemoryRegion dram[AX3000_NUM_BANKS]; Ax3000ClkState ax3000_clk; CadenceUARTState uart[AX3000_NUM_UARTS]; + CadenceGPIOState gpio[AX3000_NUM_GPIOS]; AxiadoSDHCIState sdhci0; } Ax3000SoCState; @@ -72,6 +84,15 @@ enum Ax3000Irqs { AX3000_UART3_IRQ = 170, AX3000_SDHCI0_IRQ = 123, + + AX3000_GPIO0_IRQ = 183, + AX3000_GPIO1_IRQ = 184, + AX3000_GPIO2_IRQ = 185, + AX3000_GPIO3_IRQ = 186, + AX3000_GPIO4_IRQ = 187, + AX3000_GPIO5_IRQ = 188, + AX3000_GPIO6_IRQ = 189, + AX3000_GPIO7_IRQ = 190, }; #endif /* AXIADO_AX3000_H */ From 000ef0d13b4fb82a0e4279d6409df805fc28815b Mon Sep 17 00:00:00 2001 From: Simon Xu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 455/860] hw/arm/armsse.c: fix SSE-300 s32kwatchdog address The SSE-300 SLOWCLK Secure Watchdog Timer is only defined at address 0x5802e000 in the secure region. Page 45 of the SSE-300 TRM specifies that "the watchdog is Secure access only". SSE-300 TRM: https://support.arm.com/documentation/101773/latest/ Fixes: 8901bb414a24 ("hw/arm/armsse: Add SSE-300 support") Suggested-by: Peter Maydell Signed-off-by: Simon Xu Message-id: 20260805191257.11303-2-simonxhy0404@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/armsse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index ddb210c895..e99b491bd9 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -409,7 +409,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "s32kwatchdog", .type = TYPE_CMSDK_APB_WATCHDOG, .index = 0, - .addr = 0x4802e000, + .addr = 0x5802e000, .ppc = NO_PPC, .irq = NMI_0, .slowclk = true, From 024c539a625c4cb15bc5e2f50b4a91672eef3f77 Mon Sep 17 00:00:00 2001 From: Simon Xu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 456/860] hw/arm/armsse.c: fix SSE-300 PPU addresses The SSE-300 CPU0_PPU, MGMT_PPU, DEBUG_PPU had the wrong addresses that were the same as the SSE-200 addresses. Page 146 of the SSE-300 TRM defines the addresses of the PPUs. SSE-300 TRM: https://support.arm.com/documentation/101773/latest/ Fixes: 8901bb414a24 ("hw/arm/armsse: Add SSE-300 support") Signed-off-by: Simon Xu Message-id: 20260805191257.11303-3-simonxhy0404@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/armsse.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index e99b491bd9..55fbc2c290 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -452,7 +452,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "CPU0CORE_PPU", .type = TYPE_UNIMPLEMENTED_DEVICE, .index = 2, - .addr = 0x50023000, + .addr = 0x58023000, .size = 0x1000, .ppc = NO_PPC, .irq = NO_IRQ, @@ -461,7 +461,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "MGMT_PPU", .type = TYPE_UNIMPLEMENTED_DEVICE, .index = 3, - .addr = 0x50028000, + .addr = 0x58028000, .size = 0x1000, .ppc = NO_PPC, .irq = NO_IRQ, @@ -470,7 +470,7 @@ static const ARMSSEDeviceInfo sse300_devices[] = { .name = "DEBUG_PPU", .type = TYPE_UNIMPLEMENTED_DEVICE, .index = 4, - .addr = 0x50029000, + .addr = 0x58029000, .size = 0x1000, .ppc = NO_PPC, .irq = NO_IRQ, From 230edd14d5554bd35fe5ffee37a34437973ed9c2 Mon Sep 17 00:00:00 2001 From: Simon Xu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 457/860] hw/arm/mps2-tz.c: fix AN547 APB PPC EXP0 MPC ports The AN547 TRM defines them to be on ports [15:13], not [2:0]. AN547 TRM: https://developer.arm.com/documentation/dai0547/latest/ Fixes: eb09d533d87f ("hw/arm/mps2-tz: Add new mps3-an547 board") Suggested-by: Peter Maydell Signed-off-by: Simon Xu Message-id: 20260805191257.11303-4-simonxhy0404@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/mps2-tz.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index f101c1b7c3..81e64a91de 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1048,6 +1048,19 @@ static void mps2tz_common_init(MachineState *machine) const PPCInfo an547_ppcs[] = { { .name = "apb_ppcexp0", .ports = { + { /* port 0 USER MEM APB0 */ }, + { /* port 1 USER MEM APB0 */ }, + { /* port 2 reserved */ }, + { /* port 3 reserved */ }, + { /* port 4 NPU APB0 */ }, + { /* port 5 NPU APB1 */ }, + { /* port 6 reserved */ }, + { /* port 7 reserved */ }, + { /* port 8 reserved */ }, + { /* port 9 reserved */ }, + { /* port 10 reserved */ }, + { /* port 11 reserved */ }, + { /* port 12 reserved */ }, { "ssram-mpc", make_mpc, &mms->mpc[0], 0x57000000, 0x1000 }, { "qspi-mpc", make_mpc, &mms->mpc[1], 0x57001000, 0x1000 }, { "ddr-mpc", make_mpc, &mms->mpc[2], 0x57002000, 0x1000 }, From f43d9507194c81e76ef2828ef90a3b2102890c20 Mon Sep 17 00:00:00 2001 From: Simon Xu Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 458/860] hw/arm/mps2-tz.c: add AN547 AHB PPC EXP1 DMA ports Page 42 of the AN547 TRM defines the AHB PPC EXP1 ports with DMA 1-3 AN547 TRM: https://developer.arm.com/documentation/dai0547/latest/ Suggested-by: Peter Maydell Signed-off-by: Simon Xu Message-id: 20260805191257.11303-5-simonxhy0404@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/mps2-tz.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 81e64a91de..d6d178642b 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1113,6 +1113,14 @@ static void mps2tz_common_init(MachineState *machine) { /* port 7 USER AHB interface 3 */ }, { "eth-usb", make_eth_usb, NULL, 0x41400000, 0x200000, { 49 } }, }, + }, { + .name = "ahb_ppcexp1", + .ports = { + { /* port 0 reserved */ }, + { "dma1", make_dma, &mms->dma[1], 0x41201000, 0x1000, { 62, 60, 61 } }, + { "dma2", make_dma, &mms->dma[2], 0x41202000, 0x1000, { 65, 63, 64 } }, + { "dma3", make_dma, &mms->dma[3], 0x41203000, 0x1000, { 68, 66, 67 } }, + }, }, }; From 20bf099da7c0dff3494cc36ae43add1ff376be52 Mon Sep 17 00:00:00 2001 From: botszhuang Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 459/860] hw/i2c/bcm2835_i2c: Correct iomem size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last valid register is the Clock Stretch Timeout (CLKT) at offset 0x1c. Since it is a 32-bit register, the total memory region size should be 0x1c + 4 = 0x20. Update the size parameter in memory_region_init_io() from 0x24 to 0x20 to accurately reflect the hardware specification. Suggested-by: Philippe Mathieu-Daudé Signed-off-by: botszhuang Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260804144611.31735-1-huang.botsz@gmail.com Signed-off-by: Peter Maydell --- hw/i2c/bcm2835_i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i2c/bcm2835_i2c.c b/hw/i2c/bcm2835_i2c.c index 34de1f36e5..5f6093087d 100644 --- a/hw/i2c/bcm2835_i2c.c +++ b/hw/i2c/bcm2835_i2c.c @@ -222,7 +222,7 @@ static void bcm2835_i2c_realize(DeviceState *dev, Error **errp) s->bus = i2c_init_bus(dev, NULL); memory_region_init_io(&s->iomem, OBJECT(dev), &bcm2835_i2c_ops, s, - TYPE_BCM2835_I2C, 0x24); + TYPE_BCM2835_I2C, 0x20); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } From 21fcfb6046082a06b82c17688634a1b769ee63a0 Mon Sep 17 00:00:00 2001 From: Marcelo Manzo Date: Tue, 11 Aug 2026 20:14:11 +0100 Subject: [PATCH 460/860] hw/misc/bcm2835_powermgt: implement a real watchdog timer The RSTC register's write-config bits (0x30) being set to the "full reset" value (0x20) does not mean "reset now" -- it arms the hardware watchdog so that a reset happens if the WDOG countdown register is not refreshed before it expires. The previous implementation treated any such RSTC write as an immediate reset, regardless of the WDOG value. This is dormant on older/lighter userspace (nothing in Bullseye's default boot touches these registers this way), but modern systemd (observed with Debian 13/Trixie's systemd 257) writes to RSTC as part of routine early-boot watchdog probing. With the old code, this fires an immediate reset a few seconds into boot; combined with -no-reboot this looks exactly like a QEMU crash (clean exit, no panic, no guest reboot message) with the last log line being the RSTC/WDOG write. Fix this by actually implementing the watchdog as a QEMUTimer: writes to RSTC/WDOG (re)compute the timeout from the WDOG register (in units of 1/65536 s, per the real hardware) and arm a timer for that many nanoseconds out; only when the timer actually fires do we request a system reset or shutdown, matching real hardware behavior. Clearing the write-config bits or the WDOG value disarms the timer, and reset disarms it too. Verified against real Raspberry Pi OS images under the patched raspi4b machine: Bullseye (5.15) and Bookworm (6.12) never exercised this path either way; Trixie (6.18, systemd 257) no longer crashes at boot and reaches a working login/SSH state. This is a migration compatibility break for the raspi boards. Signed-off-by: Marcelo Manzo [PMM: bump vmstate version IDs, note migration break in commit msg] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/bcm2835_powermgt.c | 53 ++++++++++++++++++++++-------- include/hw/misc/bcm2835_powermgt.h | 2 ++ 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/hw/misc/bcm2835_powermgt.c b/hw/misc/bcm2835_powermgt.c index 3ec7abad0e..7b01be48bb 100644 --- a/hw/misc/bcm2835_powermgt.c +++ b/hw/misc/bcm2835_powermgt.c @@ -19,10 +19,40 @@ #define PASSWORD_MASK 0xff000000 #define R_RSTC 0x1c -#define V_RSTC_RESET 0x20 +#define V_RSTC_WRCFG_MASK 0x30 +#define V_RSTC_FULL_RESET 0x20 #define R_RSTS 0x20 #define V_RSTS_POWEROFF 0x555 /* Linux uses partition 63 to indicate halt. */ #define R_WDOG 0x24 +#define V_WDOG_TIME_MASK 0xfffff +#define WDOG_TICKS_PER_SECOND 65536 + +static void bcm2835_powermgt_expire(void *opaque) +{ + BCM2835PowerMgtState *s = opaque; + + if ((s->rsts & 0xfff) == V_RSTS_POWEROFF) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } else { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } +} + +static void bcm2835_powermgt_update_wdog(BCM2835PowerMgtState *s) +{ + uint64_t timeout_ns; + + if ((s->rstc & V_RSTC_WRCFG_MASK) != V_RSTC_FULL_RESET || + s->wdog == 0) { + timer_del(s->wdog_timer); + return; + } + + timeout_ns = muldiv64(s->wdog, NANOSECONDS_PER_SECOND, + WDOG_TICKS_PER_SECOND); + timer_mod(s->wdog_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + timeout_ns); +} static uint64_t bcm2835_powermgt_read(void *opaque, hwaddr offset, unsigned size) @@ -70,13 +100,7 @@ static void bcm2835_powermgt_write(void *opaque, hwaddr offset, switch (offset) { case R_RSTC: s->rstc = value; - if (value & V_RSTC_RESET) { - if ((s->rsts & 0xfff) == V_RSTS_POWEROFF) { - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); - } else { - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - } - } + bcm2835_powermgt_update_wdog(s); break; case R_RSTS: qemu_log_mask(LOG_UNIMP, @@ -84,9 +108,8 @@ static void bcm2835_powermgt_write(void *opaque, hwaddr offset, s->rsts = value; break; case R_WDOG: - qemu_log_mask(LOG_UNIMP, - "bcm2835_powermgt_write: WDOG\n"); - s->wdog = value; + s->wdog = value & V_WDOG_TIME_MASK; + bcm2835_powermgt_update_wdog(s); break; default: @@ -107,12 +130,13 @@ static const MemoryRegionOps bcm2835_powermgt_ops = { static const VMStateDescription vmstate_bcm2835_powermgt = { .name = TYPE_BCM2835_POWERMGT, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32(rstc, BCM2835PowerMgtState), VMSTATE_UINT32(rsts, BCM2835PowerMgtState), VMSTATE_UINT32(wdog, BCM2835PowerMgtState), + VMSTATE_TIMER_PTR(wdog_timer, BCM2835PowerMgtState), VMSTATE_END_OF_LIST() } }; @@ -124,6 +148,8 @@ static void bcm2835_powermgt_init(Object *obj) memory_region_init_io(&s->iomem, obj, &bcm2835_powermgt_ops, s, TYPE_BCM2835_POWERMGT, 0x200); sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); + s->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + bcm2835_powermgt_expire, s); } static void bcm2835_powermgt_reset(DeviceState *dev) @@ -134,6 +160,7 @@ static void bcm2835_powermgt_reset(DeviceState *dev) s->rstc = 0x00000102; s->rsts = 0x00001000; s->wdog = 0x00000000; + timer_del(s->wdog_timer); } static void bcm2835_powermgt_class_init(ObjectClass *klass, const void *data) diff --git a/include/hw/misc/bcm2835_powermgt.h b/include/hw/misc/bcm2835_powermgt.h index fb0740c01e..d1903a9cce 100644 --- a/include/hw/misc/bcm2835_powermgt.h +++ b/include/hw/misc/bcm2835_powermgt.h @@ -12,6 +12,7 @@ #define BCM2835_POWERMGT_H #include "hw/core/sysbus.h" +#include "qemu/timer.h" #include "qom/object.h" #define TYPE_BCM2835_POWERMGT "bcm2835-powermgt" @@ -24,6 +25,7 @@ struct BCM2835PowerMgtState { uint32_t rstc; uint32_t rsts; uint32_t wdog; + QEMUTimer *wdog_timer; }; #endif From c59b6916269d8d813399bfc02c75207b0bef91c6 Mon Sep 17 00:00:00 2001 From: Marcelo Manzo Date: Tue, 11 Aug 2026 10:35:38 -0400 Subject: [PATCH 461/860] hw/arm/raspi4b: fix guest never seeing more than ~1 GiB of RAM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit raspi4_modify_dtb() decides whether to add a second memory node above the 1 GiB peripheral hole by checking info->ram_size -- but that field is the boot loader's RAM budget for loading the kernel/initrd/dtb image, itself always capped to at most UPPER_RAM_BASE - vcram_size by raspi_base_machine_init(). Since that capped value can never exceed UPPER_RAM_BASE by construction, the condition was never true for any raspi4b configuration, and the second node was never added: the guest never saw more than ~1 GiB of its nominal RAM, regardless of the machine's actual size. board_ram_size(info->board_id), computed one line above in the same function, is the value that was actually needed -- the board's real total RAM, not the boot loader's own budget for where it's allowed to place the kernel image. Confirmed via direct measurement inside the guest ("free -h" / /proc/meminfo) on raspi4b's default 2 GiB configuration, before and after: before: MemTotal: 943524 kB (~921 MiB) after: MemTotal: 1905824 kB (~1861 MiB) Also verified against two real, unmodified Raspberry Pi OS releases (Debian 11/Bullseye and Debian 13/Trixie): both now report ~1.8 GiB of usable RAM instead of ~900 MiB, with clean boots, working SSH, and no kernel errors on either. Signed-off-by: Marcelo Manzo Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260811143539.7835-2-marcelomanzo@gmail.com Signed-off-by: Peter Maydell --- hw/arm/raspi4b.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index 06aeb8db01..e1595a875f 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -85,7 +85,7 @@ static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) ram_size = board_ram_size(info->board_id); - if (info->ram_size > UPPER_RAM_BASE) { + if (ram_size > UPPER_RAM_BASE) { raspi_add_memory_node(fdt, UPPER_RAM_BASE, ram_size - UPPER_RAM_BASE); } } From 49aecf39008ebeb86127b5055cefa17d68370b20 Mon Sep 17 00:00:00 2001 From: Marcelo Manzo Date: Tue, 11 Aug 2026 10:35:39 -0400 Subject: [PATCH 462/860] tests/functional/aarch64: add raspi4b full-RAM regression test Guards against the bug fixed in the previous commit: boots raspi4b's default 2 GiB configuration and checks that the guest actually sees close to that (>1.9M kB), not the ~921 MiB the bug left it capped at. Deliberately checks a threshold rather than the exact byte count of either figure, since the precise number depends on how this specific pinned kernel accounts for its own early reservations; the threshold is comfortably between the two (943524 kB broken, 1905824 kB fixed, confirmed by hand against this exact kernel/initrd). Folded into the existing test_arm_raspi4_initrd test rather than a new standalone boot, since it needs no machine state that test isn't already setting up, and the functional-test suite is already slow enough from how many separate guest boots it runs. Signed-off-by: Marcelo Manzo Message-id: 20260811143539.7835-3-marcelomanzo@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- tests/functional/aarch64/test_raspi4.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/functional/aarch64/test_raspi4.py b/tests/functional/aarch64/test_raspi4.py index 7a4302b0c5..bacecbbe9c 100755 --- a/tests/functional/aarch64/test_raspi4.py +++ b/tests/functional/aarch64/test_raspi4.py @@ -86,6 +86,23 @@ class Aarch64Raspi4Machine(LinuxKernelTest): 'BCM2835') exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', 'cprman@7e101000') + + # We used to get the RAM size wrong; guard against a regression + # (see git history for details). + mem_total_kb = None + cmd_output = exec_command_and_wait_for_pattern( + self, 'cat /proc/meminfo', 'MemAvailable') + for line in cmd_output.decode('ascii', errors='replace').splitlines(): + if line.startswith('MemTotal:'): + mem_total_kb = int(line.split()[1]) + break + self.assertIsNotNone(mem_total_kb, 'MemTotal line not found') + self.assertGreater(mem_total_kb, 1900000, + 'guest RAM (%d kB) is far below the ~1.9 GiB ' + 'expected for a 2 GiB raspi4b -- the second ' + 'memory node above the 1 GiB peripheral hole ' + 'is probably not being added' % mem_total_kb) + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') # TODO: Raspberry Pi4 doesn't shut down properly with recent kernels # Wait for VM to shut down gracefully From ac7fa2e9d457ff9c777be32617f3c46548c4cadf Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 28 Jul 2026 17:04:14 -0400 Subject: [PATCH 463/860] migration/multifd: Validate next_packet_size in zlib/zstd recv The zlib and zstd multifd compression backends read next_packet_size from the incoming migration stream and use it directly as the read length into a fixed-size buffer (MULTIFD_PACKET_SIZE * 2 = 1MB). A malicious migration source can set next_packet_size bigger than allocated, causing a heap buffer overflow write on the destination. Add a check against zbuff_len before reading, matching what the qatzip backend already does. Also replace the assert(in_size == 0) for empty packets with proper error reporting, since the value is wire-controlled, meanwhile assert() stops working with -DNDEBUG builds. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3737 Reported-by: xlabai Reported-by: Jules Denardou Reported-by: Tristan Madani Reported-by: david korczynski (@david1766) Reported-by: huntr bubble (@bubblehuntr) Cc: qemu-stable Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20260728210417.1925078-3-peterx@redhat.com Signed-off-by: Peter Xu --- migration/multifd-zlib.c | 11 ++++++++++- migration/multifd-zstd.c | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 8820b2a787..400146566e 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -216,10 +216,19 @@ static int multifd_zlib_recv(MultiFDRecvParams *p, Error **errp) return -1; } + if (in_size > z->zbuff_len) { + error_setg(errp, "multifd %u: next_packet_size %"PRIu32 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len); + return -1; + } + multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index 3c2dcf76b0..69ef1a5f38 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -210,10 +210,19 @@ static int multifd_zstd_recv(MultiFDRecvParams *p, Error **errp) return -1; } + if (in_size > z->zbuff_len) { + error_setg(errp, "multifd %u: next_packet_size %"PRIu32 + " exceeds allocated %"PRIu32, p->id, in_size, z->zbuff_len); + return -1; + } + multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } From cf45083edc2b760a952836187aba7ec4139418c0 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 28 Jul 2026 17:04:15 -0400 Subject: [PATCH 464/860] migration/multifd: Replace assert() with error_setg() in recv paths QPL and UADK multifd backends use assert() to validate wire-controlled fields like per-page compressed lengths and packet size consistency. These asserts will stop working with -DNDEBUG builds, so may stop working. Replace all assert() calls in the receive path with proper error_setg() so validation failures are reported gracefully rather than crashing or silently ignored. While at it, touch up an assert() in qatzip recv path too. Cc: qemu-stable Cc: Yuan Liu Cc: Yichen Wang Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20260728210417.1925078-4-peterx@redhat.com Signed-off-by: Peter Xu --- migration/multifd-qatzip.c | 5 ++++- migration/multifd-qpl.c | 24 ++++++++++++++++++++---- migration/multifd-uadk.c | 24 ++++++++++++++++++++---- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/migration/multifd-qatzip.c b/migration/multifd-qatzip.c index 7419e5dc0d..0262e81eac 100644 --- a/migration/multifd-qatzip.c +++ b/migration/multifd-qatzip.c @@ -348,7 +348,10 @@ static int qatzip_recv(MultiFDRecvParams *p, Error **errp) multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c index 52902eb00c..3826e7f340 100644 --- a/migration/multifd-qpl.c +++ b/migration/multifd-qpl.c @@ -664,26 +664,42 @@ static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) } multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } /* read compressed page lengths */ len = p->normal_num * sizeof(uint32_t); - assert(len < in_size); + if (len >= in_size) { + error_setg(errp, "multifd %u: header len %"PRIu32 + " >= packet size %"PRIu32, p->id, len, in_size); + return -1; + } ret = qio_channel_read_all(p->c, (void *) qpl->zlen, len, errp); if (ret != 0) { return ret; } for (int i = 0; i < p->normal_num; i++) { qpl->zlen[i] = be32_to_cpu(qpl->zlen[i]); - assert(qpl->zlen[i] <= multifd_ram_page_size()); + if (qpl->zlen[i] > multifd_ram_page_size()) { + error_setg(errp, "multifd %u: page %d compressed len %" + PRIu32" too large", p->id, i, qpl->zlen[i]); + return -1; + } zbuf_len += qpl->zlen[i]; ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); } /* read compressed pages */ - assert(in_size == len + zbuf_len); + if (in_size != len + zbuf_len) { + error_setg(errp, "multifd %u: packet size %"PRIu32 + " != header %"PRIu32" + data %"PRIu32, + p->id, in_size, len, zbuf_len); + return -1; + } ret = qio_channel_read_all(p->c, (void *) qpl->zbuf, zbuf_len, errp); if (ret != 0) { return ret; diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index fd7cd9b5e8..d373615ba8 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -245,12 +245,19 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) multifd_recv_zero_page_process(p); if (!p->normal_num) { - assert(in_size == 0); + if (in_size != 0) { + error_setg(errp, "multifd %u: expected empty packet", p->id); + return -1; + } return 0; } /* read compressed data lengths */ - assert(hdr_len < in_size); + if (hdr_len >= in_size) { + error_setg(errp, "multifd %u: header len %"PRIu32 + " >= packet size %"PRIu32, p->id, hdr_len, in_size); + return -1; + } ret = qio_channel_read_all(p->c, (void *) uadk_data->buf_hdr, hdr_len, errp); if (ret != 0) { @@ -259,12 +266,21 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) for (int i = 0; i < p->normal_num; i++) { uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]); + if (uadk_data->buf_hdr[i] > page_size) { + error_setg(errp, "multifd %u: page %d compressed len %"PRIu32 + " too large", p->id, i, uadk_data->buf_hdr[i]); + return -1; + } data_len += uadk_data->buf_hdr[i]; - assert(uadk_data->buf_hdr[i] <= page_size); } /* read compressed data */ - assert(in_size == hdr_len + data_len); + if (in_size != hdr_len + data_len) { + error_setg(errp, "multifd %u: packet size %"PRIu32 + " != header %"PRIu32" + data %"PRIu32, + p->id, in_size, hdr_len, data_len); + return -1; + } ret = qio_channel_read_all(p->c, (void *)buf, data_len, errp); if (ret != 0) { return ret; From 93676b24721135ae965169dd5797d37a5e022939 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 23 Jul 2026 18:38:06 +0200 Subject: [PATCH 465/860] hw: add compat machines for 11.2 Add 11.2 machine types for arm/i440fx/loongarch/m68k/q35/s390x/spapr. Signed-off-by: Cornelia Huck Reviewed-by: Eric Farman Reviewed-by: Bibo Mao Link: https://lore.kernel.org/qemu-devel/20260723163806.368127-1-cohuck@redhat.com [farman@linux.ibm.com: fixup hw/core/machine.c hunk] Signed-off-by: Eric Farman --- hw/arm/virt.c | 11 +++++++++-- hw/core/machine.c | 3 +++ hw/i386/pc.c | 3 +++ hw/i386/pc_piix.c | 13 +++++++++++-- hw/i386/pc_q35.c | 13 +++++++++++-- hw/loongarch/virt.c | 13 ++++++++++--- hw/m68k/virt.c | 11 +++++++++-- hw/ppc/spapr.c | 17 ++++++++++++++--- hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++- include/hw/core/boards.h | 3 +++ include/hw/i386/pc.h | 3 +++ 11 files changed, 89 insertions(+), 15 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 219597cabd..e7a56e37f7 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -4441,10 +4441,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) static void virt_machine_11_0_options(MachineClass *mc) { diff --git a/hw/core/machine.c b/hw/core/machine.c index 73b4d82b4a..3b15645304 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -40,6 +40,9 @@ #include "qemu/audio.h" #include "hw/arm/smmuv3.h" +GlobalProperty hw_compat_11_1[] = {}; +const size_t hw_compat_11_1_len = G_N_ELEMENTS(hw_compat_11_1); + GlobalProperty hw_compat_11_0[] = { { "virtio-mmio", VIRTIO_QUEUE_SIZE_OVERRIDE, "1024" }, { "chardev-vc", "encoding", "cp437" }, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f064aa2b3e..e9e4fc262b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -74,6 +74,9 @@ #include "hw/xen/xen-bus.h" #endif +GlobalProperty pc_compat_11_1[] = {}; +const size_t pc_compat_11_1_len = G_N_ELEMENTS(pc_compat_11_1); + GlobalProperty pc_compat_11_0[] = {}; const size_t pc_compat_11_0_len = G_N_ELEMENTS(pc_compat_11_0); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 82457bdb16..223a19cc4b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -428,12 +428,21 @@ static void pc_i440fx_machine_options(MachineClass *m) pc_piix_compat_defaults, pc_piix_compat_defaults_len); } -static void pc_i440fx_machine_11_1_options(MachineClass *m) +static void pc_i440fx_machine_11_2_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(11, 1); +DEFINE_I440FX_MACHINE_AS_LATEST(11, 2); + +static void pc_i440fx_machine_11_1_options(MachineClass *m) +{ + pc_i440fx_machine_11_2_options(m); + compat_props_add(m->compat_props, hw_compat_11_1, hw_compat_11_1_len); + compat_props_add(m->compat_props, pc_compat_11_1, pc_compat_11_1_len); +} + +DEFINE_I440FX_MACHINE(11, 1); static void pc_i440fx_machine_11_0_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6c1e4eff5f..94cd71124b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -383,12 +383,21 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_11_1_options(MachineClass *m) +static void pc_q35_machine_11_2_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(11, 1); +DEFINE_Q35_MACHINE_AS_LATEST(11, 2); + +static void pc_q35_machine_11_1_options(MachineClass *m) +{ + pc_q35_machine_11_2_options(m); + compat_props_add(m->compat_props, hw_compat_11_1, hw_compat_11_1_len); + compat_props_add(m->compat_props, pc_compat_11_1, pc_compat_11_1_len); +} + +DEFINE_Q35_MACHINE(11, 1); static void pc_q35_machine_11_0_options(MachineClass *m) { diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 6693dea647..9cc79929a8 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1465,7 +1465,6 @@ static void virt_class_init(ObjectClass *oc, const void *data) mc->default_ram_id = "loongarch.ram"; mc->desc = "QEMU LoongArch Virtual Machine"; mc->max_cpus = LOONGARCH_MAX_CPUS; - mc->is_default = 1; mc->default_kernel_irqchip_split = false; mc->block_default_type = IF_VIRTIO; mc->default_boot_order = "c"; @@ -1546,6 +1545,7 @@ static void virt_class_init(ObjectClass *oc, const void *data) MACHINE_VER_DEPRECATION(__VA_ARGS__); \ if (latest) { \ mc->alias = "virt"; \ + mc->is_default = true; \ } \ } \ static const TypeInfo MACHINE_VER_SYM(info, virt, __VA_ARGS__) = \ @@ -1600,7 +1600,14 @@ static void machvirt_machine_init(void) type_init(machvirt_machine_init); -static void virt_machine_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 51158ce1c0..274970978f 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_11_1_options(MachineClass *mc) +static void virt_machine_11_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(11, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(11, 2) + +static void virt_machine_11_1_options(MachineClass *mc) +{ + virt_machine_11_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} +DEFINE_VIRT_MACHINE(11, 1) static void virt_machine_11_0_options(MachineClass *mc) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b79828b4e9..7e284edd7d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4764,14 +4764,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) /* - * pseries-11.1 + * pseries-11.2 */ -static void spapr_machine_11_1_class_options(MachineClass *mc) +static void spapr_machine_11_2_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE_AS_LATEST(11, 1); +DEFINE_SPAPR_MACHINE_AS_LATEST(11, 2); + +/* + * pseries-11.1 + */ +static void spapr_machine_11_1_class_options(MachineClass *mc) +{ + spapr_machine_11_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); +} + +DEFINE_SPAPR_MACHINE(11, 1); /* * pseries-11.0 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 25a9fa4955..55131c1a44 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -925,14 +925,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_11_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_11_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(11, 2); + static void ccw_machine_11_1_instance_options(MachineState *machine) { + ccw_machine_11_2_instance_options(machine); } static void ccw_machine_11_1_class_options(MachineClass *mc) { + ccw_machine_11_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_11_1, hw_compat_11_1_len); } -DEFINE_CCW_MACHINE_AS_LATEST(11, 1); +DEFINE_CCW_MACHINE(11, 1); static void ccw_machine_11_0_instance_options(MachineState *machine) { diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h index 29c68931d8..a436d48c8e 100644 --- a/include/hw/core/boards.h +++ b/include/hw/core/boards.h @@ -815,6 +815,9 @@ compat_props_add(GPtrArray *arr, } } +extern GlobalProperty hw_compat_11_1[]; +extern const size_t hw_compat_11_1_len; + extern GlobalProperty hw_compat_11_0[]; extern const size_t hw_compat_11_0_len; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index d4b6d3ed57..ac03da97b6 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -209,6 +209,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_11_1[]; +extern const size_t pc_compat_11_1_len; + extern GlobalProperty pc_compat_11_0[]; extern const size_t pc_compat_11_0_len; From 9b3d68edb24d4e04f49822c6e11641e4eb23dfd5 Mon Sep 17 00:00:00 2001 From: Eric Farman Date: Wed, 12 Aug 2026 11:13:28 -0400 Subject: [PATCH 466/860] pc-bios/s390-ccw.img: update s390x bios Update the s390 bios with recent fixes for out-of-bounds accesses. Signed-off-by: Eric Farman --- pc-bios/s390-ccw.img | Bin 104136 -> 104216 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index e24a93e14a880aa3a5d498fc84c7bdd1a0964d7c..291cd77eaff40679d0cc2d5697d0c7b73c493e94 100644 GIT binary patch literal 104216 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 UvU8h)7H+<)GeKI&Z>B%-za>;eNdN!< From 80776c4df92c3d58f89608b61e355092a7d524ed Mon Sep 17 00:00:00 2001 From: Bin Guo Date: Tue, 4 Aug 2026 15:34:45 +0800 Subject: [PATCH 467/860] hw/intc/loongarch_pch_pic: Validate htmsi_vector before indexing parent_irq pch_pic_update_irq() used the guest-writable htmsi_vector[irq] value as an index into parent_irq[] without checking bounds. A value >= irq_num (64 in the array, but only 32 are used by the virt machine) causes an out-of-bounds read and a guest-triggerable QEMU crash. Validate the vector before calling qemu_set_irq() in both the raise and lower paths and log a guest error if it is out of range. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4114 Cc: qemu-stable@nongnu.org Signed-off-by: Bin Guo Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/intc/loongarch_pch_pic.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 82e16be391..e87c7497c1 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -19,13 +19,21 @@ static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, { uint64_t val; int irq; + uint8_t vector; if (level) { val = mask & s->intirr & ~s->int_mask; if (val) { irq = ctz64(val); + vector = s->htmsi_vector[irq]; + if (vector >= s->irq_num) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: htmsi_vector[%d]=%u out of range\n", + __func__, irq, vector); + return; + } s->intisr |= MAKE_64BIT_MASK(irq, 1); - qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); + qemu_set_irq(s->parent_irq[vector], 1); } } else { /* @@ -35,8 +43,15 @@ static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask, val = mask & s->intisr & ~s->intirr; if (val) { irq = ctz64(val); + vector = s->htmsi_vector[irq]; + if (vector >= s->irq_num) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: htmsi_vector[%d]=%u out of range\n", + __func__, irq, vector); + return; + } s->intisr &= ~MAKE_64BIT_MASK(irq, 1); - qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); + qemu_set_irq(s->parent_irq[vector], 0); } } } From 181b584b7765fac97a3caac3fe0121fc379d779a Mon Sep 17 00:00:00 2001 From: Dongyan Qian Date: Tue, 4 Aug 2026 11:30:34 +0800 Subject: [PATCH 468/860] tests/acpi: Allow LoongArch virt MADT changes The LoongArch virt MADT revision will be corrected to match the interrupt controller structures it contains. Allow the APIC and APIC.topology test data to change so that the source change can be reviewed separately from the regenerated binary tables. Signed-off-by: Dongyan Qian Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- 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 dfb8523c8b..0e18673fcd 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/loongarch64/virt/APIC", +"tests/data/acpi/loongarch64/virt/APIC.topology", From 9583a9c07d9d91439e5921ae7fb69bd7f69a94f3 Mon Sep 17 00:00:00 2001 From: Dongyan Qian Date: Tue, 4 Aug 2026 11:30:35 +0800 Subject: [PATCH 469/860] hw/loongarch/virt: Set MADT revision to 6 The LoongArch virt machine emits Core PIC, EIO PIC, MSI PIC and BIO PIC subtables, but advertises MADT revision 1. Revision 1 predates these LoongArch interrupt controller structures. ACPI 6.5 introduced the LoongArch interrupt controller structures and defined MADT revision 6. ACPI 6.6 raises the MADT revision to 7 for the additional RISC-V interrupt controller structures, while leaving the LoongArch structures unchanged. Since the virt machine emits only the LoongArch structures defined by ACPI 6.5, set the MADT header revision to 6, the minimum revision that describes the table contents. Fixes: 735143f10d3 ("hw/loongarch: Add acpi ged support") Signed-off-by: Dongyan Qian Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- hw/loongarch/virt-acpi-build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index a0b445f297..600ef60fc5 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -128,7 +128,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); int i, arch_id, flags; - AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lvms->oem_id, + AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = lvms->oem_id, .oem_table_id = lvms->oem_table_id }; acpi_table_begin(&table, table_data); From c94267793c10ef111663d8c2e85e0331ff8a6800 Mon Sep 17 00:00:00 2001 From: Dongyan Qian Date: Tue, 4 Aug 2026 11:30:36 +0800 Subject: [PATCH 470/860] tests/acpi: Update LoongArch virt MADT Regenerate the APIC and APIC.topology test data after correcting the LoongArch virt MADT revision. The generated tables change only Header.Revision from 1 to 6 and the corresponding checksum. Their lengths and interrupt controller subtables remain unchanged. Signed-off-by: Dongyan Qian Signed-off-by: Bibo Mao Reviewed-by: Bibo Mao --- tests/data/acpi/loongarch64/virt/APIC | Bin 108 -> 108 bytes tests/data/acpi/loongarch64/virt/APIC.topology | Bin 153 -> 153 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/tests/data/acpi/loongarch64/virt/APIC b/tests/data/acpi/loongarch64/virt/APIC index 3477789f422cad54f16734b3ec9ad1ff5135165d..e90c267e1fdd83143edc732df41ca5659415090d 100644 GIT binary patch delta 16 Xcmd1F;c^V{bk1R5U|{<&kt+!RBvAxk delta 16 Xcmd1F;c^V{bk1R5U|{?@kt+!RBuoTf diff --git a/tests/data/acpi/loongarch64/virt/APIC.topology b/tests/data/acpi/loongarch64/virt/APIC.topology index da0089d57f608aca230a76b67e72f9f0ad8e71f2..d00b7241c7ad76867781d5649e2de5fb6a5f45bc 100644 GIT binary patch delta 18 ZcmbQqIFpgfF~HM#CIbTlTi`^legG=>1a<%b delta 18 ZcmbQqIFpgfF~HM#CIbTlW7tHlegG=+1a<%b diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 0e18673fcd..dfb8523c8b 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/loongarch64/virt/APIC", -"tests/data/acpi/loongarch64/virt/APIC.topology", From 77b91aca6148fafcade41516f12a3309f1e1f552 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 14 Jul 2026 10:55:16 +0800 Subject: [PATCH 471/860] target/loongarch: Set timer tick value even if disabled If constant timer is enabled, its tick value is remained value from the next expired time. However if timer is not enabled, its value should be CONSTANT_TIMER_TICK_MASK or zero. Signed-off-by: Bibo Mao Reviewed-by: Xianglai Li --- target/loongarch/tcg/constant_timer.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/target/loongarch/tcg/constant_timer.c b/target/loongarch/tcg/constant_timer.c index f56e76d482..b91ad1de69 100644 --- a/target/loongarch/tcg/constant_timer.c +++ b/target/loongarch/tcg/constant_timer.c @@ -22,12 +22,18 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu) uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu) { + CPULoongArchState *env = &cpu->env; + CPUSysState *sys = env_sys(env); uint64_t now, expire; - now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - expire = timer_expire_time_ns(&cpu->timer); + if ((sys->CSR_TCFG & CONSTANT_TIMER_ENABLE) && + (sys->CSR_TVAL < sys->CSR_TCFG)) { + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + expire = timer_expire_time_ns(&cpu->timer); + sys->CSR_TVAL = (expire - now) / TIMER_PERIOD; + } - return (expire - now) / TIMER_PERIOD; + return sys->CSR_TVAL; } void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, @@ -42,8 +48,10 @@ void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; timer_mod(&cpu->timer, next); + sys->CSR_TVAL = sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK; } else { timer_del(&cpu->timer); + sys->CSR_TVAL = 0; } } @@ -58,8 +66,9 @@ void loongarch_constant_timer_cb(void *opaque) now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + (sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD; timer_mod(&cpu->timer, next); + sys->CSR_TVAL = sys->CSR_TCFG & CONSTANT_TIMER_TICK_MASK; } else { - sys->CSR_TCFG = FIELD_DP64(sys->CSR_TCFG, CSR_TCFG, EN, 0); + sys->CSR_TVAL = CONSTANT_TIMER_TICK_MASK; } loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1); From 10dd206e92e0f688b7aa411e57533448c56ab6b8 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 28 Jul 2026 17:04:17 -0400 Subject: [PATCH 472/860] migration/ram: Check for RAMBlock size mismatch when parsing Add an underflow check for the subtract of total RAMBlock size to make sure it won't underflow. It should not happen in production systems but only if the migration stream was hijacked, which is not a real concern since migration channel is trusted. Still protect against it. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4013 Reported-by: Tristan Madani Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20260728210417.1925078-6-peterx@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index 8918b2f03b..b6eb842746 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4263,15 +4263,15 @@ static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length) return ret; } -static int parse_ramblocks(QEMUFile *f, ram_addr_t total_ram_bytes) +static int parse_ramblocks(QEMUFile *f, uint64_t total_ram_bytes) { int ret = 0; /* Synchronize RAM block list */ - while (!ret && total_ram_bytes) { + while (total_ram_bytes) { RAMBlock *block; char id[256]; - ram_addr_t length; + uint64_t length; int len = qemu_get_byte(f); qemu_get_buffer(f, (uint8_t *)id, len); @@ -4285,8 +4285,15 @@ static int parse_ramblocks(QEMUFile *f, ram_addr_t total_ram_bytes) error_report("Unknown ramblock \"%s\", cannot accept " "migration", id); ret = -EINVAL; + break; + } + + if (usub64_overflow(total_ram_bytes, length, &total_ram_bytes)) { + error_report("%s: RAMBlock '%s' size underflow total RAM size", + __func__, block->idstr); + ret = -EFAULT; + break; } - total_ram_bytes -= length; } return ret; From 89f15c1362970150bf5711cdb84b9742c0db0cd8 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 12 Aug 2026 08:43:27 -0400 Subject: [PATCH 473/860] migration: Fix rare hang of migration_channel_read_peek() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In an unlikely case, when a migration stream is attached to the destination QEMU and only send <4 bytes to the channel as magic, it's possible that migration_channel_read_peek() may spin forever. Fix it by adding a manual sleep for partial read. Since the path isn't attached to a coroutine, it means when partial read happens, there's yet not much we can do but hang the main thread, it will happen even for len==0 case. It means monitors can hang due to this, either partial read or no data arrived (but connection established). Leave this for later, the hope is this is extremely rare in production. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3889 Reported-by: Feifan Qian Cc: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20260812124327.2572363-1-peterx@redhat.com Signed-off-by: Peter Xu --- migration/channel.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/migration/channel.c b/migration/channel.c index 1e2935f926..266ae8f776 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -296,9 +296,16 @@ int migration_channel_read_peek(QIOChannel *ioc, if (len == buflen) { break; + } else if (len == QIO_CHANNEL_ERR_BLOCK) { + qio_channel_wait_cond(ioc, G_IO_IN); + } else { + /* + * When partially ready, we can't use qio_channel_wait_cond() + * because it will return immediately. Apply a manual wait. + */ + assert(!qemu_in_coroutine()); + g_usleep(1000); } - - qio_channel_wait_cond(ioc, G_IO_IN); } return 0; From ebec7c73229831c6574c70fec3cc28c778566277 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 19 Jun 2026 02:25:22 +0200 Subject: [PATCH 474/860] tests/qtest/ide-test: parametrize the ATAPI CD-ROM read test cdrom_pio_impl() and test_cdrom_dma() duplicate the same image setup and data-integrity check around two different transfer mechanisms. Fold them into a single cdrom_read_impl(nblocks, flags) helper, with a CDROM_PIO/CDROM_DMA flag selecting the transfer, so further read coverage can be added once for both paths. No functional change: /ide/cdrom/pio, pio_large and dma run exactly as before. Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 143 +++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 77 deletions(-) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 721e78170b..c97bc17b67 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -1034,8 +1034,12 @@ static void ide_wait_intr(QTestState *qts, int irq) g_assert_not_reached(); } -static void cdrom_pio_impl(int nblocks) +#define CDROM_PIO 0 +#define CDROM_DMA (1 << 0) + +static void cdrom_read_impl(int nblocks, unsigned flags) { + bool dma = flags & CDROM_DMA; QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; @@ -1063,57 +1067,75 @@ static void cdrom_pio_impl(int nblocks) dev = get_pci_device(qts, &bmdma_bar, &ide_bar); qtest_irq_intercept_in(qts, "ioapic"); - /* PACKET command on device 0 */ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); - qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); - /* HP0: Check_Status_A State */ - nsleep(qts, 400); - data = ide_wait_clear(qts, BSY); - /* HP1: Send_Packet State */ - assert_bit_set(data, DRQ | DRDY); - assert_bit_clear(data, ERR | DF | BSY); + if (dma) { + uintptr_t guest_buf = guest_alloc(&guest_malloc, rxsize); + PrdtEntry prdt[1]; - /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ - send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); + prdt[0].addr = cpu_to_le32(guest_buf); + prdt[0].size = cpu_to_le32(rxsize | PRDT_EOT); - /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. - * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. - * We allow an odd limit only when the remaining transfer size is - * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only - * request n blocks, so our request size is always even. - * For this reason, we assume there is never a hanging byte to fetch. */ - g_assert(!(rxsize & 1)); - limit = BYTE_COUNT_LIMIT & ~1; - for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { - size_t offset = i * (limit / 2); - size_t rem = (rxsize / 2) - offset; + send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_PACKET, 0, + nblocks, prdt, ARRAY_SIZE(prdt), + send_scsi_cdb_read10); - /* HP3: INTRQ_Wait */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); - - /* HP2: Check_Status_B (and clear IRQ) */ + qtest_memread(qts, guest_buf, rx, rxsize); + } else { + /* PACKET command on device 0 */ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); + qpci_io_writeb(dev, ide_bar, reg_lba_high, + (BYTE_COUNT_LIMIT >> 8 & 0xFF)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); + /* HP0: Check_Status_A State */ + nsleep(qts, 400); data = ide_wait_clear(qts, BSY); + /* HP1: Send_Packet State */ assert_bit_set(data, DRQ | DRDY); assert_bit_clear(data, ERR | DF | BSY); - /* HP4: Transfer_Data */ - for (j = 0; j < MIN((limit / 2), rem); j++) { - rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, - reg_data)); + /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ + send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); + + /* + * Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. + * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. + * We allow an odd limit only when the remaining transfer size is + * less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only + * request n blocks, so our request size is always even. + * For this reason, we assume there is never a hanging byte to fetch. + */ + g_assert(!(rxsize & 1)); + limit = BYTE_COUNT_LIMIT & ~1; + for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) { + size_t offset = i * (limit / 2); + size_t rem = (rxsize / 2) - offset; + + /* HP3: INTRQ_Wait */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* HP2: Check_Status_B (and clear IRQ) */ + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ | DRDY); + assert_bit_clear(data, ERR | DF | BSY); + + /* HP4: Transfer_Data */ + for (j = 0; j < MIN((limit / 2), rem); j++) { + rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, + reg_data)); + } } + + /* Check for final completion IRQ */ + ide_wait_intr(qts, IDE_PRIMARY_IRQ); + + /* Sanity check final state */ + data = ide_wait_clear(qts, DRQ); + assert_bit_set(data, DRDY); + assert_bit_clear(data, DRQ | ERR | DF | BSY); } - /* Check for final completion IRQ */ - ide_wait_intr(qts, IDE_PRIMARY_IRQ); - - /* Sanity check final state */ - data = ide_wait_clear(qts, DRQ); - assert_bit_set(data, DRDY); - assert_bit_clear(data, DRQ | ERR | DF | BSY); - g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); + g_free(pattern); g_free(rx); test_bmdma_teardown(qts); @@ -1122,51 +1144,18 @@ static void cdrom_pio_impl(int nblocks) static void test_cdrom_pio(void) { - cdrom_pio_impl(1); + cdrom_read_impl(1, CDROM_PIO); } static void test_cdrom_pio_large(void) { /* Test a few loops of the PIO DRQ mechanism. */ - cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE); + cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_PIO); } - static void test_cdrom_dma(void) { - QTestState *qts; - static const size_t len = ATAPI_BLOCK_SIZE; - size_t ret; - char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16); - char *rx = g_malloc0(len); - uintptr_t guest_buf; - PrdtEntry prdt[1]; - FILE *fh; - - qts = ide_test_start( - "-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " - "-device ide-cd,drive=sr0,bus=ide.0", tmp_path[0]); - qtest_irq_intercept_in(qts, "ioapic"); - - guest_buf = guest_alloc(&guest_malloc, len); - prdt[0].addr = cpu_to_le32(guest_buf); - prdt[0].size = cpu_to_le32(len | PRDT_EOT); - - generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE); - fh = fopen(tmp_path[0], "wb+"); - ret = fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh); - g_assert_cmpint(ret, ==, 16); - fclose(fh); - - send_dma_request(qts, CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10); - - /* Read back data from guest memory into local qtest memory */ - qtest_memread(qts, guest_buf, rx, len); - g_assert_cmpint(memcmp(pattern, rx, len), ==, 0); - - g_free(pattern); - g_free(rx); - test_bmdma_teardown(qts); + cdrom_read_impl(1, CDROM_DMA); } int main(int argc, char **argv) From fe543c2d787a611f1efa34107818a8eee90bb5dc Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 19 Jun 2026 02:28:51 +0200 Subject: [PATCH 475/860] tests/qtest/ide-test: add a multi-sector ATAPI DMA read test test_cdrom_pio_large() already exercises a multi-sector PIO read. Add the DMA counterpart through the same cdrom_read_impl() helper so the multi-block ATAPI DMA read path gets equivalent coverage. Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index c97bc17b67..6e59197566 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -1158,6 +1158,11 @@ static void test_cdrom_dma(void) cdrom_read_impl(1, CDROM_DMA); } +static void test_cdrom_dma_large(void) +{ + cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_DMA); +} + int main(int argc, char **argv) { const char *base; @@ -1217,6 +1222,7 @@ int main(int argc, char **argv) qtest_add_func("/ide/cdrom/pio", test_cdrom_pio); qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large); qtest_add_func("/ide/cdrom/dma", test_cdrom_dma); + qtest_add_func("/ide/cdrom/dma_large", test_cdrom_dma_large); ret = g_test_run(); From 502dbdfd9687e967dfe5eefce7123ecf9baa798c Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 19 Jun 2026 02:29:11 +0200 Subject: [PATCH 476/860] tests/qtest/ide-test: cover raw (2352-byte) ATAPI CD reads READ CD with the field-selector set to 0xf8 returns full 2352-byte raw sectors (sync + header + 2048 data + EDC/ECC), driving the ATAPI raw read path that READ10 never touches. Add a send_scsi_cdb_read_cd() helper and a CDROM_RAW flag to cdrom_read_impl(), then exercise both PIO and DMA. The PIO case uses a byte-count limit spanning several raw sectors so the device must rebuffer mid-burst, and each sector's 2048-byte payload is verified at its in-sector offset. Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 76 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 6e59197566..b36b0f8875 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -48,6 +48,10 @@ #define ATAPI_BLOCK_SIZE 2048 +/* Raw READ CD sector: 12 sync + 4 header + 2048 data + 288 EDC/ECC. */ +#define ATAPI_RAW_SIZE 2352 +#define ATAPI_RAW_DATA 16 + /* How many bytes to receive via ATAPI PIO at one time. * Must be less than 0xFFFF. */ #define BYTE_COUNT_LIMIT 5120 @@ -982,6 +986,41 @@ static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar, } } +typedef struct ReadCDCDB { + uint8_t opcode; + uint8_t sector_type; + uint32_t lba; + uint8_t length[3]; + uint8_t main_channel; + uint8_t sub_channel; + uint8_t control; +} __attribute__((__packed__)) ReadCDCDB; + +static void send_scsi_cdb_read_cd(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t lba, int nblocks) +{ + ReadCDCDB pkt = { }; + int i; + + g_assert_cmpint(lba, <=, UINT32_MAX); + g_assert_cmpint(nblocks, >=, 0); + g_assert_cmpint(nblocks, <=, 0xffffff); + + /* Construct SCSI CDB packet */ + pkt.opcode = 0xbe; + pkt.lba = cpu_to_be32(lba); + pkt.length[0] = (nblocks >> 16) & 0xff; + pkt.length[1] = (nblocks >> 8) & 0xff; + pkt.length[2] = nblocks & 0xff; + pkt.main_channel = 0xf8; /* sync + headers + user data + EDC/ECC: 2352 */ + + /* Send Packet */ + for (i = 0; i < sizeof(ReadCDCDB) / 2; i++) { + qpci_io_writew(dev, ide_bar, reg_data, + le16_to_cpu(((uint16_t *)&pkt)[i])); + } +} + static void nsleep(QTestState *qts, int64_t nsecs) { const struct timespec val = { .tv_nsec = nsecs }; @@ -1036,10 +1075,12 @@ static void ide_wait_intr(QTestState *qts, int irq) #define CDROM_PIO 0 #define CDROM_DMA (1 << 0) +#define CDROM_RAW (1 << 1) static void cdrom_read_impl(int nblocks, unsigned flags) { bool dma = flags & CDROM_DMA; + bool raw = flags & CDROM_RAW; QTestState *qts; QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; @@ -1047,8 +1088,11 @@ static void cdrom_read_impl(int nblocks, unsigned flags) int patt_blocks = MAX(16, nblocks); size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; char *pattern = g_malloc(patt_len); - size_t rxsize = ATAPI_BLOCK_SIZE * nblocks; + unsigned xfer = raw ? ATAPI_RAW_SIZE : ATAPI_BLOCK_SIZE; + size_t rxsize = xfer * nblocks; uint16_t *rx = g_malloc0(rxsize); + void (*send_cdb)(QPCIDevice *, QPCIBar, uint64_t, int) = + raw ? send_scsi_cdb_read_cd : send_scsi_cdb_read10; int i, j; uint8_t data; uint16_t limit; @@ -1075,8 +1119,7 @@ static void cdrom_read_impl(int nblocks, unsigned flags) prdt[0].size = cpu_to_le32(rxsize | PRDT_EOT); send_dma_request_dev(qts, dev, bmdma_bar, ide_bar, CMD_PACKET, 0, - nblocks, prdt, ARRAY_SIZE(prdt), - send_scsi_cdb_read10); + nblocks, prdt, ARRAY_SIZE(prdt), send_cdb); qtest_memread(qts, guest_buf, rx, rxsize); } else { @@ -1093,8 +1136,7 @@ static void cdrom_read_impl(int nblocks, unsigned flags) assert_bit_set(data, DRQ | DRDY); assert_bit_clear(data, ERR | DF | BSY); - /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ - send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); + send_cdb(dev, ide_bar, 0, nblocks); /* * Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. @@ -1134,7 +1176,17 @@ static void cdrom_read_impl(int nblocks, unsigned flags) assert_bit_clear(data, DRQ | ERR | DF | BSY); } - g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); + if (raw) { + /* The 2048-byte payload of each raw sector sits past its header. */ + for (i = 0; i < nblocks; i++) { + uint8_t *sec = (uint8_t *)rx + i * ATAPI_RAW_SIZE + ATAPI_RAW_DATA; + + g_assert_cmpint(memcmp(sec, pattern + i * ATAPI_BLOCK_SIZE, + ATAPI_BLOCK_SIZE), ==, 0); + } + } else { + g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0); + } g_free(pattern); g_free(rx); @@ -1163,6 +1215,16 @@ static void test_cdrom_dma_large(void) cdrom_read_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE, CDROM_DMA); } +static void test_cdrom_pio_raw(void) +{ + cdrom_read_impl(4, CDROM_RAW); +} + +static void test_cdrom_dma_raw(void) +{ + cdrom_read_impl(4, CDROM_DMA | CDROM_RAW); +} + int main(int argc, char **argv) { const char *base; @@ -1223,6 +1285,8 @@ int main(int argc, char **argv) qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large); qtest_add_func("/ide/cdrom/dma", test_cdrom_dma); qtest_add_func("/ide/cdrom/dma_large", test_cdrom_dma_large); + qtest_add_func("/ide/cdrom/pio_raw", test_cdrom_pio_raw); + qtest_add_func("/ide/cdrom/dma_raw", test_cdrom_dma_raw); ret = g_test_run(); From de86ef3dfd337ec5a74b43d7d042ab937d0166bf Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 19 Jun 2026 02:29:11 +0200 Subject: [PATCH 477/860] tests/qtest/libqos/ahci: support raw (2352-byte) READ CD ahci_exec() always builds ATAPI commands with a 2048-byte logical sector size, so it cannot drive a READ CD that returns full 2352-byte raw sectors. Add an atapi_raw option that sets the READ CD field-selector to 0xf8 and the command's sector size to 2352 before the transfer is sized, so the derived block count stays correct while the buffer and byte counts cover the raw sectors. Signed-off-by: Denis V. Lunev --- tests/qtest/libqos/ahci.c | 5 +++++ tests/qtest/libqos/ahci.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index 0621a6c477..e3019a1eca 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -707,6 +707,11 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, if (opts->atapi) { uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE; cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma); + if (opts->atapi_raw) { + /* request full 2352-byte raw sectors; sector_size must match */ + cmd->atapi_cmd[9] = 0xf8; + cmd->sector_size = ATAPI_RAW_SECTOR_SIZE; + } } else { cmd = ahci_command_create(op); } diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h index a0487a1557..2a48a7523d 100644 --- a/tests/qtest/libqos/ahci.h +++ b/tests/qtest/libqos/ahci.h @@ -242,6 +242,7 @@ #define AHCI_SECTOR_SIZE (512) #define ATAPI_SECTOR_SIZE (2048) +#define ATAPI_RAW_SECTOR_SIZE (2352) #define AHCI_SIGNATURE_CDROM (0xeb140101) #define AHCI_SIGNATURE_DISK (0x00000101) @@ -485,6 +486,7 @@ typedef struct AHCIOpts { uint64_t buffer; /* Pointer to source or destination guest buffer */ bool atapi; /* ATAPI command? */ bool atapi_dma; /* Use DMA for ATAPI? */ + bool atapi_raw; /* READ CD returning 2352-byte raw sectors */ bool error; int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *); From 31f13e71afd1cfb55b02c6977c829f0813ab439c Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 19 Jun 2026 02:29:12 +0200 Subject: [PATCH 478/860] tests/qtest/ahci: cover raw (2352-byte) ATAPI CD reads Add /ahci/cdrom/{pio,dma}/raw: read several sectors with READ CD in raw mode (atapi_raw), so the ATAPI 2352-byte unpack path is exercised through the AHCI delivery, which IDE coverage does not reach. Each sector's 2048-byte payload is verified at its in-sector offset. The PIO case uses a byte-count limit of one raw sector per DRQ burst: libqos asserts a one-sector PIO transfer, and the multi-sector unpack loop is already covered by the IDE raw test. Signed-off-by: Denis V. Lunev --- tests/qtest/ahci-test.c | 68 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 44799eea15..58bc04b3ef 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -1565,6 +1565,31 @@ static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd, return 0; } +static int ahci_cb_cmp_raw(AHCIQState *ahci, AHCICommand *cmd, + const AHCIOpts *opts) +{ + unsigned char *tx = opts->opaque; + unsigned char *rx; + unsigned i, nsectors; + + if (!opts->size) { + return 0; + } + + nsectors = opts->size / ATAPI_RAW_SECTOR_SIZE; + rx = g_malloc0(opts->size); + qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size); + /* Each raw sector carries its 2048-byte payload past a 16-byte header. */ + for (i = 0; i < nsectors; i++) { + g_assert_cmphex(memcmp(rx + i * ATAPI_RAW_SECTOR_SIZE + 16, + tx + i * ATAPI_SECTOR_SIZE, + ATAPI_SECTOR_SIZE), ==, 0); + } + g_free(rx); + + return 0; +} + static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd, bool override_bcl, uint16_t bcl) { @@ -1625,6 +1650,47 @@ static void test_cdrom_pio_multi(void) ahci_test_cdrom_read10(3, false); } +static void ahci_test_cdrom_raw(int nsectors, bool dma) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + AHCIOpts opts = { + .size = (uint64_t)ATAPI_RAW_SECTOR_SIZE * nsectors, + .atapi = true, + .atapi_dma = dma, + .atapi_raw = true, + .set_bcl = true, + .bcl = ATAPI_RAW_SECTOR_SIZE, /* one raw sector per DRQ burst */ + .post_cb = ahci_cb_cmp_raw, + }; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * (nsectors + 1); + + fd = prepare_iso(iso_size, &tx, &iso); + opts.opaque = tx; + + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + + ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_CD, &opts); + + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_cdrom_dma_raw(void) +{ + ahci_test_cdrom_raw(3, true); +} + +static void test_cdrom_pio_raw(void) +{ + ahci_test_cdrom_raw(3, false); +} + /* * Regression test: a buffered ATAPI read completing after a command * engine restart must not dereference the cleared cur_cmd. Cover both @@ -2100,8 +2166,10 @@ int main(int argc, char **argv) qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma); qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi); + qtest_add_func("/ahci/cdrom/dma/raw", test_cdrom_dma_raw); qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio); qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi); + qtest_add_func("/ahci/cdrom/pio/raw", test_cdrom_pio_raw); qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl); qtest_add_func("/ahci/cdrom/eject", test_atapi_tray); From 12289dc700d910bca4b79bb8de71acff06097489 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 19 Jun 2026 02:29:48 +0200 Subject: [PATCH 479/860] hw/ide/atapi: read the whole elementary transfer asynchronously An ATAPI PIO read whose byte-count limit spans more than one CD sector must fetch the later sectors of a DRQ burst from inside the completion of the first, asynchronous read. cd_read_sector_sync() did this with a synchronous blk_pread(), which runs blk_wait_while_drained() before issuing the request. If a drain is in progress when that completion runs -- as happens when a guest reset reaches virtio_blk_stop_ioeventfd() -> bdrv_drain_all_begin() while an ATAPI read is in flight on the same QEMU -- the nested read is queued until the drained section ends while the outer completion still holds blk->in_flight. bdrv_drain_all_begin() then waits forever for that in_flight count to drop: the main loop is wedged in the drain with the BQL held, and every other QMP/monitor operation blocks behind it. Read the whole elementary transfer in a single asynchronous request up front instead, so no read is ever issued in the middle of a burst. cd_read_sector() now reads all the sectors a burst spans (the raw 2352-byte case is unpacked in place on completion) and cd_read_sector_sync() is removed. The DMA path already batched its reads and is unchanged. Signed-off-by: Denis V. Lunev --- hw/ide/atapi.c | 180 +++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 96 deletions(-) diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index a42b748521..0ea149ad8c 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -88,46 +88,14 @@ static void cd_data_to_raw(uint8_t *buf, int lba) memset(buf, 0, 288); } -static int -cd_read_sector_sync(IDEState *s) -{ - int ret; - block_acct_start(blk_get_stats(s->blk), &s->acct, - ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); - - trace_cd_read_sector_sync(s->lba); - - switch (s->cd_sector_size) { - case 2048: - ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - ATAPI_SECTOR_SIZE, s->io_buffer, 0); - break; - case 2352: - ret = blk_pread(s->blk, (int64_t)s->lba << ATAPI_SECTOR_BITS, - ATAPI_SECTOR_SIZE, s->io_buffer + 16, 0); - if (ret >= 0) { - cd_data_to_raw(s->io_buffer, s->lba); - } - break; - default: - block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); - return -EIO; - } - - if (ret < 0) { - block_acct_failed(blk_get_stats(s->blk), &s->acct); - } else { - block_acct_done(blk_get_stats(s->blk), &s->acct); - s->lba++; - s->io_buffer_index = 0; - } - - return ret; -} - static void cd_read_sector_cb(void *opaque, int ret) { IDEState *s = opaque; + int et = s->elementary_transfer_size; + int skip = s->io_buffer_index; + int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size); + uint8_t *buf; + int i; trace_cd_read_sector_cb(s->lba, ret); @@ -140,34 +108,64 @@ static void cd_read_sector_cb(void *opaque, int ret) block_acct_done(blk_get_stats(s->blk), &s->acct); if (s->cd_sector_size == 2352) { - cd_data_to_raw(s->io_buffer, s->lba); + /* unpack back-to-front so a sector never clobbers an unmoved one */ + for (i = nsec - 1; i >= 0; i--) { + memmove(s->io_buffer + i * 2352 + 16, s->io_buffer + i * 2048, + ATAPI_SECTOR_SIZE); + cd_data_to_raw(s->io_buffer + i * 2352, s->lba + i); + } } - s->lba++; - s->io_buffer_index = 0; s->status &= ~BUSY_STAT; - ide_atapi_cmd_reply_end(s); + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + s->lcyl = et & 0xff; + s->hcyl = (et >> 8) & 0xff; + ide_bus_set_irq(s->bus); + + /* a boundary sector shared with the next burst is re-read there */ + buf = s->io_buffer + skip; + s->packet_transfer_size -= et; + s->lba += (skip + et) / s->cd_sector_size; + s->io_buffer_index = (skip + et) % s->cd_sector_size; + s->elementary_transfer_size = 0; + + if (ide_transfer_start_norecurse(s, buf, et, ide_atapi_cmd_reply_end)) { + ide_atapi_cmd_reply_end(s); + } } +/* + * Read the whole elementary transfer (one DRQ burst) in a single async + * request. No read is issued mid-burst, so unlike the old synchronous + * rebuffer it cannot deadlock against a concurrent drain. + */ static int cd_read_sector(IDEState *s) { - void *buf; + int et = s->elementary_transfer_size; + int skip = s->io_buffer_index; + int nsec = DIV_ROUND_UP(skip + et, s->cd_sector_size); if (s->cd_sector_size != 2048 && s->cd_sector_size != 2352) { block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_READ); return -EINVAL; } - buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer; - qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE); + /* a burst is bounded by the byte count limit, so it fits io_buffer */ + assert(nsec * s->cd_sector_size <= s->io_buffer_total_len); + + /* + * Read the payload packed at the front of io_buffer; the 2352 raw case is + * unpacked into place on completion. + */ + qemu_iovec_init_buf(&s->qiov, s->io_buffer, nsec * ATAPI_SECTOR_SIZE); trace_cd_read_sector(s->lba); block_acct_start(blk_get_stats(s->blk), &s->acct, - ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); + nsec * ATAPI_SECTOR_SIZE, BLOCK_ACCT_READ); - ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, 4, + ide_buffered_readv(s, (int64_t)s->lba << 2, &s->qiov, nsec * 4, cd_read_sector_cb, s); s->status |= BUSY_STAT; @@ -222,59 +220,49 @@ static uint16_t atapi_byte_count_limit(IDEState *s) void ide_atapi_cmd_reply_end(IDEState *s) { int byte_count_limit, size, ret; - while (s->packet_transfer_size > 0) { - trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, - s->elementary_transfer_size, - s->io_buffer_index); - /* see if a new sector must be read */ - if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { - if (!s->elementary_transfer_size) { - ret = cd_read_sector(s); - if (ret < 0) { - ide_atapi_io_error(s, ret); - } - return; - } else { - /* rebuffering within an elementary transfer is - * only possible with a sync request because we - * end up with a race condition otherwise */ - ret = cd_read_sector_sync(s); - if (ret < 0) { - ide_atapi_io_error(s, ret); - return; - } + trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, + s->elementary_transfer_size, + s->io_buffer_index); + + if (s->lba != -1 && s->packet_transfer_size > 0) { + byte_count_limit = atapi_byte_count_limit(s); + trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) { + byte_count_limit--; } + size = byte_count_limit; } - if (s->elementary_transfer_size > 0) { - /* there are some data left to transmit in this elementary - transfer */ - size = s->cd_sector_size - s->io_buffer_index; - if (size > s->elementary_transfer_size) - size = s->elementary_transfer_size; - } else { - /* a new transfer is needed */ - s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; - ide_bus_set_irq(s->bus); - byte_count_limit = atapi_byte_count_limit(s); - trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); - size = s->packet_transfer_size; - if (size > byte_count_limit) { - /* byte count limit must be even if this case */ - if (byte_count_limit & 1) - byte_count_limit--; - size = byte_count_limit; - } - s->lcyl = size & 0xff; - s->hcyl = size >> 8; - s->elementary_transfer_size = size; - /* we cannot transmit more than one sector at a time */ - if (s->lba != -1) { - if (size > (s->cd_sector_size - s->io_buffer_index)) - size = (s->cd_sector_size - s->io_buffer_index); - } - trace_ide_atapi_cmd_reply_end_new(s, s->status); + s->elementary_transfer_size = size; + ret = cd_read_sector(s); + if (ret < 0) { + ide_atapi_io_error(s, ret); } + return; + } + + while (s->packet_transfer_size > 0) { + /* a new transfer is needed */ + s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + ide_bus_set_irq(s->bus); + byte_count_limit = atapi_byte_count_limit(s); + trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); + size = s->packet_transfer_size; + if (size > byte_count_limit) { + /* byte count limit must be even if this case */ + if (byte_count_limit & 1) { + byte_count_limit--; + } + size = byte_count_limit; + } + s->lcyl = size & 0xff; + s->hcyl = size >> 8; + s->elementary_transfer_size = size; + trace_ide_atapi_cmd_reply_end_new(s, s->status); + s->packet_transfer_size -= size; s->elementary_transfer_size -= size; s->io_buffer_index += size; @@ -329,7 +317,7 @@ static void ide_atapi_cmd_read_pio(IDEState *s, int lba, int nb_sectors, s->lba = lba; s->packet_transfer_size = nb_sectors * sector_size; s->elementary_transfer_size = 0; - s->io_buffer_index = sector_size; + s->io_buffer_index = 0; s->cd_sector_size = sector_size; ide_atapi_cmd_reply_end(s); From be1ee6a8f6e3b7192d57e900497b81a048f8d193 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 19 Jun 2026 02:31:11 +0200 Subject: [PATCH 480/860] tests/qtest/ahci: regression test for ATAPI read vs. drain Add /ahci/cdrom/drain/{pio,dma}: issue a multi-sector ATAPI read whose byte-count limit spans two sectors so the device must rebuffer in the middle of the DRQ burst, hold the backend read in flight with a blkdebug delay, and fire x-blockdev-set-iothread -- which runs bdrv_drain_all_begin() exactly like a guest reset does through virtio_blk_stop_ioeventfd(). On the unfixed PIO path the nested sector fetch is queued behind the drain and the main loop wedges, so the test hangs. The DMA variant never rebuffers and serves as a sanity twin. Signed-off-by: Denis V. Lunev --- tests/qtest/ahci-test.c | 66 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 58bc04b3ef..71d23fe56a 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -1754,6 +1754,70 @@ static void test_atapi_engine_restart_dma(void) test_atapi_engine_restart_in_flight(true); } +/* + * Regression test: a multi-sector ATAPI read fetches its later sectors from + * inside the first read's completion; a concurrent drain (as a guest reset + * triggers via bdrv_drain_all_begin) must not wedge on that nested read. + * blkdebug keeps the read in flight across x-blockdev-set-iothread. + */ +static void test_atapi_drain_in_flight(bool dma) +{ + AHCIQState *ahci; + AHCICommand *cmd; + unsigned char *tx; + char *iso; + int fd; + uint8_t port; + uint64_t buffer; + uint16_t bcl = ATAPI_SECTOR_SIZE * 2; + uint64_t iso_size = (uint64_t)ATAPI_SECTOR_SIZE * 3; + + fd = prepare_iso(iso_size, &tx, &iso); + + /* 1s read delay: a wide margin so the drain starts before it completes */ + ahci = ahci_boot_and_enable( + "-blockdev driver=file,node-name=file0,filename=%s,read-only=on " + "-blockdev driver=blkdebug,node-name=cd0,image=file0,read-only=on," + "inject-error.0.event=none,inject-error.0.iotype=read," + "inject-error.0.errno=0,inject-error.0.delay-ns=1000000000 " + "-M q35 " + "-device ide-cd,drive=cd0 ", iso); + port = ahci_port_select(ahci); + + buffer = ahci_alloc(ahci, bcl); + qtest_memset(ahci->parent->qts, buffer, 0x00, bcl); + + cmd = ahci_atapi_command_create(CMD_ATAPI_READ_10, bcl, dma); + ahci_command_adjust(cmd, 0, buffer, bcl, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Drain (all nodes) while the delayed read is still in flight. */ + qtest_qmp_assert_success(ahci->parent->qts, + "{ 'execute': 'x-blockdev-set-iothread'," + " 'arguments': { 'node-name': 'cd0', 'iothread': null," + " 'force': true } }"); + + /* Round-trip through the device to confirm qemu is still alive. */ + ahci_px_rreg(ahci, port, AHCI_PX_TFD); + + ahci_command_free(cmd); + ahci_free(ahci, buffer); + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void test_atapi_drain_pio(void) +{ + test_atapi_drain_in_flight(false); +} + +static void test_atapi_drain_dma(void) +{ + test_atapi_drain_in_flight(true); +} + /* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0 * completes as a NOP instead of erroring out. */ static void test_atapi_bcl(void) @@ -2177,6 +2241,8 @@ int main(int argc, char **argv) test_atapi_engine_restart_pio); qtest_add_func("/ahci/cdrom/engine_restart/dma", test_atapi_engine_restart_dma); + qtest_add_func("/ahci/cdrom/drain/pio", test_atapi_drain_pio); + qtest_add_func("/ahci/cdrom/drain/dma", test_atapi_drain_dma); ret = g_test_run(); From ac84b9ec9604db04a5c3a5935eff8caf85d01597 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 29 Jul 2026 12:12:52 +0300 Subject: [PATCH 481/860] tests/functional: add skipWithoutSudo() decorator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To be used in the next commit: that would be a test for TAP networking, and it will need to setup TAP device. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Tested-by: Lei Yang Reviewed-by: Maksim Davydov Reviewed-by: Ben Chaney Message-ID: <20260729091334.1863155-15-vsementsov@yandex-team.ru> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/decorators.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index fcf236ecfd..aa135acc78 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -6,6 +6,7 @@ import importlib import os import platform import resource +import subprocess from unittest import skipIf, skipUnless from .cmd import which @@ -177,3 +178,18 @@ def skipLockedMemoryTest(locked_memory): ulimit_memory == resource.RLIM_INFINITY or ulimit_memory >= locked_memory * 1024, f'Test required {locked_memory} kB of available locked memory', ) + +''' +Decorator to skip execution of a test if passwordless +sudo command is not available. +''' +def skipWithoutSudo(): + proc = subprocess.run(["sudo", "-n", "/bin/true"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + check=False) + + return skipUnless(proc.returncode == 0, + f'requires password-less sudo access: {proc.stdout}') From c347711ea7f5b9cbcd558ecfaa81988e0baff359 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 29 Jul 2026 12:31:42 +0300 Subject: [PATCH 482/860] tests/functional/qemu_test: drop *args argument from .get_vm() It's redundant. Only one caller use it, and it may be simply substituted by .add_args(). Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Thomas Huth Message-ID: <20260729093146.1893719-3-vsementsov@yandex-team.ru> Signed-off-by: Thomas Huth --- tests/functional/migration.py | 3 ++- tests/functional/qemu_test/testcase.py | 9 +++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/functional/migration.py b/tests/functional/migration.py index 4344e03be4..8d2428efc2 100644 --- a/tests/functional/migration.py +++ b/tests/functional/migration.py @@ -63,7 +63,8 @@ class MigrationTest(QemuSystemTest): self.assert_dest_vm(dst_vm) def migrate(self, dst_uri, src_uri=None): - dst_vm = self.get_vm('-incoming', 'defer', name="dst-qemu") + dst_vm = self.get_vm("dst-qemu") + dst_vm.add_args('-incoming', 'defer') self.configure_machine(dst_vm) dst_vm.launch() diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index e4179d165c..12ef84281c 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -381,7 +381,7 @@ class QemuSystemTest(QemuBaseTest): if helptxt.find(devicename) < 0: self.skipTest('no support for device ' + devicename) - def _new_vm(self, name, *args): + def _new_vm(self, name): vm = QEMUMachine(self.qemu_bin, name=name, base_temp_dir=self.workdir, @@ -394,20 +394,17 @@ class QemuSystemTest(QemuBaseTest): vm.add_args("-chardev", f"socket,id=backdoor,path={sockpath},server=on,wait=off", "-mon", "chardev=backdoor,mode=control") - - if args: - vm.add_args(*args) return vm @property def vm(self): return self.get_vm(name='default') - def get_vm(self, *args, name=None): + def get_vm(self, name=None): if not name: name = str(uuid.uuid4()) if self._vms.get(name) is None: - self._vms[name] = self._new_vm(name, *args) + self._vms[name] = self._new_vm(name) if self.cpu is not None: self._vms[name].add_args('-cpu', self.cpu) if self.machine is not None: From ba6741f63da565c739c038d267fd6d9132266597 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 29 Jul 2026 12:31:44 +0300 Subject: [PATCH 483/860] tests/testcase.py: passthrough monitor_address We'll need it soon to implement test for cpr-exec mode of tap-fd-migration. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Thomas Huth Message-ID: <20260729093146.1893719-5-vsementsov@yandex-team.ru> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 12ef84281c..69d3d06cc0 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -381,11 +381,12 @@ class QemuSystemTest(QemuBaseTest): if helptxt.find(devicename) < 0: self.skipTest('no support for device ' + devicename) - def _new_vm(self, name): + def _new_vm(self, name, monitor_address): vm = QEMUMachine(self.qemu_bin, name=name, base_temp_dir=self.workdir, - log_dir=self.log_file()) + log_dir=self.log_file(), + monitor_address=monitor_address) self.log.debug('QEMUMachine "%s" created', name) self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) @@ -400,11 +401,11 @@ class QemuSystemTest(QemuBaseTest): def vm(self): return self.get_vm(name='default') - def get_vm(self, name=None): + def get_vm(self, name=None, monitor_address=None): if not name: name = str(uuid.uuid4()) if self._vms.get(name) is None: - self._vms[name] = self._new_vm(name) + self._vms[name] = self._new_vm(name, monitor_address) if self.cpu is not None: self._vms[name].add_args('-cpu', self.cpu) if self.machine is not None: From 15700a66ecb53c52717c913c2c4501372c0115ab Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 30 Jul 2026 18:39:01 +0200 Subject: [PATCH 484/860] hw/usb/dev-uas: Don't abort if guest provided an undersized buffer for status QEMU currently aborts if the guest provides an undersized buffer for the status packet (8 bytes): hw/usb/core.c:623: usb_packet_copy: Assertion `p->actual_length + bytes <= iov->size' failed. If we hit this situation, log a guest error and continue by simply only providing the bytes that the guest asked for. (Note: This is e.g. similar to the UAS_PIPE_ID_COMMAND case that also clamps the length with: length = MIN(sizeof(iu), p->iov.size)) Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3900 Reported-by: Feifan Qian Signed-off-by: Thomas Huth Message-ID: <20260730163901.1154791-1-thuth@redhat.com> --- hw/usb/dev-uas.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 963c0433b3..be8c3667e8 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -359,6 +359,7 @@ static void usb_uas_send_status_bh(void *opaque) UASDevice *uas = opaque; UASStatus *st; USBPacket *p; + uint32_t length; while ((st = QTAILQ_FIRST(&uas->results)) != NULL) { if (uas_using_streams(uas)) { @@ -373,7 +374,14 @@ static void usb_uas_send_status_bh(void *opaque) break; } - usb_packet_copy(p, &st->status, st->length); + length = st->length; + if (length > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "usb uas: packet (%zd) too small for status (%d)\n", + p->iov.size, length); + length = p->iov.size; + } + usb_packet_copy(p, &st->status, length); QTAILQ_REMOVE(&uas->results, st, next); g_free(st); @@ -875,7 +883,14 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) break; } } - usb_packet_copy(p, &st->status, st->length); + length = st->length; + if (length > p->iov.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "usb uas: packet (%zd) too small for status (%d)\n", + p->iov.size, length); + length = p->iov.size; + } + usb_packet_copy(p, &st->status, length); QTAILQ_REMOVE(&uas->results, st, next); g_free(st); break; From 46099d99005588b25eb07957cd9cdd688c4fc476 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 31 Jul 2026 13:33:52 +0200 Subject: [PATCH 485/860] hw/net/vmxnet3: Do not abort if guest provides bad interrupt numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vmxnet3_validate_interrupts() currently aborts via hw_error() if the guest provided bad interrupt numbers. This should not happen, QEMU should rather refuse to activate the device in this case instead. Thus propagate the error to the callers to handle it more gracefully there. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/539 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20260731113352.189066-1-thuth@redhat.com> --- hw/net/vmxnet3.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 8569484b2f..24c551a054 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -1336,32 +1336,46 @@ static bool vmxnet3_verify_intx(VMXNET3State *s, int intx) || intx == pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1; } -static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx) +static bool vmxnet3_validate_irq_idx(const char *type, bool is_msix, int idx) { int max_ints = is_msix ? VMXNET3_MAX_INTRS : VMXNET3_MAX_NMSIX_INTRS; + if (idx >= max_ints) { - hw_error("Bad interrupt index: %d\n", idx); + qemu_log_mask(LOG_GUEST_ERROR, + "vmxnet3: Bad %s queue interrupt index: %d\n", + type, idx); + return false; } + + return true; } -static void vmxnet3_validate_interrupts(VMXNET3State *s) +static bool vmxnet3_validate_interrupts(VMXNET3State *s) { int i; VMW_CFPRN("Verifying event interrupt index (%d)", s->event_int_idx); - vmxnet3_validate_interrupt_idx(s->msix_used, s->event_int_idx); + if (!vmxnet3_validate_irq_idx("event", s->msix_used, s->event_int_idx)) { + return false; + } for (i = 0; i < s->txq_num; i++) { int idx = s->txq_descr[i].intr_idx; VMW_CFPRN("Verifying TX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); + if (!vmxnet3_validate_irq_idx("TX", s->msix_used, idx)) { + return false; + } } for (i = 0; i < s->rxq_num; i++) { int idx = s->rxq_descr[i].intr_idx; VMW_CFPRN("Verifying RX queue %d interrupt index (%d)", i, idx); - vmxnet3_validate_interrupt_idx(s->msix_used, idx); + if (!vmxnet3_validate_irq_idx("RX", s->msix_used, idx)) { + return false; + } } + + return true; } static bool vmxnet3_validate_queues(VMXNET3State *s) @@ -1554,7 +1568,9 @@ static void vmxnet3_activate_device(VMXNET3State *s) sizeof(s->rxq_descr[i].rxq_stats)); } - vmxnet3_validate_interrupts(s); + if (!vmxnet3_validate_interrupts(s)) { + return; + } /* Make sure everything is in place before device activation */ smp_wmb(); @@ -2392,7 +2408,9 @@ static int vmxnet3_post_load(void *opaque, int version_id) if (!vmxnet3_validate_queues(s)) { return -1; } - vmxnet3_validate_interrupts(s); + if (!vmxnet3_validate_interrupts(s)) { + return -1; + } return 0; } From 4adfb431c0c3abd84909d1228b4954bfd68a1dbc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 31 Jul 2026 16:02:29 +0200 Subject: [PATCH 486/860] hw/i386/amd_iommu: Avoid undefined behavior in amdvi_setevent_bits() The code in amdvi_encode_event() calls amdvi_setevent_bits() with start = 64: amdvi_setevent_bits(evt, addr, 64, 64); and amdvi_setevent_bits() then calculates: uint64_t mask = MAKE_64BIT_MASK(start, length); but this MAKE_64BIT_MASK() macro shifts a value left by "start" bit positions. Shifting left by more than 63 is undefined behavior and could have unexpected results with different compilers / architectures. Fix it by using "bitpos" instead, which was likely the original intended behavior anyway. (bitpos is calculated as bitpos = start % 64). Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3633 Fixes: 1d5b128cbeea ("hw/iommu: Fix problems reported by Coverity scan") Reviewed-by: Alejandro Jimenez Signed-off-by: Thomas Huth Message-ID: <20260731140229.259272-1-thuth@redhat.com> --- hw/i386/amd_iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 90252c52af..578c27ccbe 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -323,7 +323,7 @@ static void amdvi_setevent_bits(uint64_t *buffer, uint64_t value, int start, int length) { int index = start / 64, bitpos = start % 64; - uint64_t mask = MAKE_64BIT_MASK(start, length); + uint64_t mask = MAKE_64BIT_MASK(bitpos, length); buffer[index] &= ~mask; buffer[index] |= (value << bitpos) & mask; } From 167882fe86d7b266bc7df27c58428ea40e10245d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 17 Jul 2026 14:51:41 +0100 Subject: [PATCH 487/860] qom/object.c: introduce OBJECT_PROPERTY_SCALAR_GETTER(type) macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro can be used to generate the boilerplate property_get_type_ptr() QOM get function for the specified scalar type. Replace the existing scaler get functions with the new macro. Signed-off-by: Mark Cave-Ayland Reviewed-by: Daniel P. Berrangé Message-Id: <20260717135254.508701-2-mark.caveayland@nutanix.com> --- qom/object.c | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/qom/object.c b/qom/object.c index f79b2cf361..34aef7ca9f 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2661,12 +2661,20 @@ static char *object_get_type(Object *obj, Error **errp) return g_strdup(object_get_typename(obj)); } -static void property_get_uint8_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint8_t value = *(uint8_t *)opaque; - visit_type_uint8(v, name, &value, errp); -} + +#define OBJECT_PROPERTY_SCALAR_GETTER(type) \ + static void property_get_##type##_ptr(Object *obj, Visitor *v, \ + const char *name, \ + void *opaque, Error **errp) \ + { \ + type##_t value = *(type##_t *)opaque; \ + visit_type_##type(v, name, &value, errp); \ + } + +OBJECT_PROPERTY_SCALAR_GETTER(uint8) +OBJECT_PROPERTY_SCALAR_GETTER(uint16) +OBJECT_PROPERTY_SCALAR_GETTER(uint32) +OBJECT_PROPERTY_SCALAR_GETTER(uint64) static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -2681,13 +2689,6 @@ static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, *field = value; } -static void property_get_uint16_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint16_t value = *(uint16_t *)opaque; - visit_type_uint16(v, name, &value, errp); -} - static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2701,13 +2702,6 @@ static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, *field = value; } -static void property_get_uint32_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t value = *(uint32_t *)opaque; - visit_type_uint32(v, name, &value, errp); -} - static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2721,13 +2715,6 @@ static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, *field = value; } -static void property_get_uint64_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint64_t value = *(uint64_t *)opaque; - visit_type_uint64(v, name, &value, errp); -} - static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { From 5f8edcbc3ca0371b1ed0b38f7191ca91ef3cf7e4 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 17 Jul 2026 14:51:42 +0100 Subject: [PATCH 488/860] qom/object.c: introduce OBJECT_PROPERTY_SCALAR_SETTER(type) macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro can be used to generate the boilerplate property_set_type_ptr() QOM set function for the specified scalar type. Replace the existing scaler set functions with the new macro. Signed-off-by: Mark Cave-Ayland Reviewed-by: Daniel P. Berrangé Message-Id: <20260717135254.508701-3-mark.caveayland@nutanix.com> --- qom/object.c | 72 ++++++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/qom/object.c b/qom/object.c index 34aef7ca9f..8a1f80f613 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2671,62 +2671,34 @@ static char *object_get_type(Object *obj, Error **errp) visit_type_##type(v, name, &value, errp); \ } + +#define OBJECT_PROPERTY_SCALAR_SETTER(type) \ + static void property_set_##type##_ptr(Object *obj, Visitor *v, \ + const char *name, \ + void *opaque, Error **errp) \ + { \ + type##_t *field = opaque; \ + type##_t value; \ + \ + if (!visit_type_##type(v, name, &value, errp)) { \ + return; \ + } \ + \ + *field = value; \ + } + OBJECT_PROPERTY_SCALAR_GETTER(uint8) +OBJECT_PROPERTY_SCALAR_SETTER(uint8) OBJECT_PROPERTY_SCALAR_GETTER(uint16) +OBJECT_PROPERTY_SCALAR_SETTER(uint16) OBJECT_PROPERTY_SCALAR_GETTER(uint32) +OBJECT_PROPERTY_SCALAR_SETTER(uint32) OBJECT_PROPERTY_SCALAR_GETTER(uint64) +OBJECT_PROPERTY_SCALAR_SETTER(uint64) -static void property_set_uint8_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint8_t *field = opaque; - uint8_t value; +#undef OBJECT_PROPERTY_SCALAR_GETTER +#undef OBJECT_PROPERTY_SCALAR_SETTER - if (!visit_type_uint8(v, name, &value, errp)) { - return; - } - - *field = value; -} - -static void property_set_uint16_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint16_t *field = opaque; - uint16_t value; - - if (!visit_type_uint16(v, name, &value, errp)) { - return; - } - - *field = value; -} - -static void property_set_uint32_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint32_t *field = opaque; - uint32_t value; - - if (!visit_type_uint32(v, name, &value, errp)) { - return; - } - - *field = value; -} - -static void property_set_uint64_ptr(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - uint64_t *field = opaque; - uint64_t value; - - if (!visit_type_uint64(v, name, &value, errp)) { - return; - } - - *field = value; -} ObjectProperty * object_property_add_uint8_ptr(Object *obj, const char *name, From 361a15cff8ce13566d06c5954a92dbda1d24eb36 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 17 Jul 2026 14:51:43 +0100 Subject: [PATCH 489/860] qom/object.c: introduce DEFINE_OBJECT_PROPERTY_SCALAR_METHODS() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This macro defines both the QOM get and set functions for the given scaler type. Replace the combined use of OBJECT_PROPERTY_SCALAR_GETTER() and OBJECT_PROPERTY_SCALAR_SETTER() with the new macro. Signed-off-by: Mark Cave-Ayland Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20260717135254.508701-4-mark.caveayland@nutanix.com> --- qom/object.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/qom/object.c b/qom/object.c index 8a1f80f613..622840f9f8 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2687,17 +2687,20 @@ static char *object_get_type(Object *obj, Error **errp) *field = value; \ } -OBJECT_PROPERTY_SCALAR_GETTER(uint8) -OBJECT_PROPERTY_SCALAR_SETTER(uint8) -OBJECT_PROPERTY_SCALAR_GETTER(uint16) -OBJECT_PROPERTY_SCALAR_SETTER(uint16) -OBJECT_PROPERTY_SCALAR_GETTER(uint32) -OBJECT_PROPERTY_SCALAR_SETTER(uint32) -OBJECT_PROPERTY_SCALAR_GETTER(uint64) -OBJECT_PROPERTY_SCALAR_SETTER(uint64) + +#define DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(type) \ + OBJECT_PROPERTY_SCALAR_GETTER(type) \ + OBJECT_PROPERTY_SCALAR_SETTER(type) + + +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint8) +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint16) +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint32) +DEFINE_OBJECT_PROPERTY_SCALAR_METHODS(uint64) #undef OBJECT_PROPERTY_SCALAR_GETTER #undef OBJECT_PROPERTY_SCALAR_SETTER +#undef DEFINE_OBJECT_PROPERTY_SCALAR_METHODS ObjectProperty * From 504be94fe34262fe04d5e36333432169741a740c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 17 Jul 2026 14:51:44 +0100 Subject: [PATCH 490/860] qom/object.h: rename @child to @targetp in object_property_add_link() documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was missed when updating the parameter name in commit 36854207f0 ("object: rename link "child" to "target""). Signed-off-by: Mark Cave-Ayland Fixes: 36854207f0 ("object: rename link "child" to "target"") Reviewed-by: Daniel P. Berrangé Message-Id: <20260717135254.508701-5-mark.caveayland@nutanix.com> --- include/qom/object.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index 11f55613fc..4e7fe2af7c 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1802,8 +1802,8 @@ void object_property_allow_set_link(const Object *obj, const char *name, * property is read-only and cannot be set. Care must be taken to handle NULL * values for @val. * - * Ownership of the pointer that @child points to is transferred to the - * link property. The reference count for *@child is + * Ownership of the pointer that @targetp points to is transferred to the + * link property. The reference count for *@targetp is * managed by the property from after the function returns till the * property is deleted with object_property_del(). If the * @flags %OBJ_PROP_LINK_STRONG bit is set, From 478542dd83ad805e1f7c9676a91541bf4aa9fd5d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 17 Jul 2026 14:51:45 +0100 Subject: [PATCH 491/860] qom/object.h: add missing documentation for object_class_* property functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is so that the object_class_* property functions appear in the generated QOM documentation at devel/qom-api.html. Signed-off-by: Mark Cave-Ayland Reviewed-by: Daniel P. Berrangé Message-Id: <20260717135254.508701-6-mark.caveayland@nutanix.com> --- include/qom/object.h | 136 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/include/qom/object.h b/include/qom/object.h index 4e7fe2af7c..272c41cac7 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -1818,6 +1818,33 @@ ObjectProperty *object_property_add_link(Object *obj, const char *name, Object *val, Error **errp), ObjectPropertyLinkFlags flags); +/** + * object_class_property_add_link: + * @oc: the object class to add a property to + * @name: the name of the property + * @type: the qobj type of the link + * @offset: the offset from the object instance where the link object reference + * is stored + * @check: callback to veto setting or NULL if the property is read-only + * @flags: additional options for the link + * + * Links establish relationships between objects. Links are unidirectional + * although two links can be combined to form a bidirectional relationship + * between objects. + * + * Links form the graph in the object model. + * + * The @check() callback is invoked when object_property_set_link() is called + * and can raise an error to prevent the link being set. If @check is NULL, the + * property is read-only and cannot be set. Care must be taken to handle NULL + * values for @val. + * + * If the @flags %OBJ_PROP_LINK_STRONG bit is set, the reference count of the + * linked object is incremented when the property is set, and decremented again + * when the property is modified. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_link(ObjectClass *oc, const char *name, const char *type, ptrdiff_t offset, @@ -1859,6 +1886,19 @@ ObjectProperty *object_property_add_str(Object *obj, const char *name, char *(*get)(Object *, Error **), void (*set)(Object *, const char *, Error **)); +/** + * object_class_property_add_str: + * @klass: the object class to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. This function must + * return a string to be freed by g_free(). + * @set: the setter or NULL if the property is read-only + * + * Add a string property using getters/setters. This function will add a + * property of type 'string'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_str(ObjectClass *klass, const char *name, char *(*get)(Object *, Error **), @@ -1881,6 +1921,18 @@ ObjectProperty *object_property_add_bool(Object *obj, const char *name, bool (*get)(Object *, Error **), void (*set)(Object *, bool, Error **)); +/** + * object_class_property_add_bool: + * @klass: the object class to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. + * @set: the setter or NULL if the property is read-only + * + * Add a bool property using getters/setters. This function will add a + * property of type 'bool'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_bool(ObjectClass *klass, const char *name, bool (*get)(Object *, Error **), @@ -1906,6 +1958,20 @@ ObjectProperty *object_property_add_enum(Object *obj, const char *name, int (*get)(Object *, Error **), void (*set)(Object *, int, Error **)); +/** + * object_class_property_add_enum: + * @klass: the object class to add a property to + * @name: the name of the property + * @typename: the name of the enum data type + * @lookup: enum value namelookup table + * @get: the getter or %NULL if the property is write-only. + * @set: the setter or %NULL if the property is read-only + * + * Add an enum property using getters/setters. This function will add a + * property of type '@typename'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_enum(ObjectClass *klass, const char *name, const char *typename, @@ -1927,6 +1993,17 @@ ObjectProperty *object_class_property_add_enum(ObjectClass *klass, ObjectProperty *object_property_add_tm(Object *obj, const char *name, void (*get)(Object *, struct tm *, Error **)); +/** + * object_class_property_add_tm: + * @klass: the object class to add a property to + * @name: the name of the property + * @get: the getter or NULL if the property is write-only. + * + * Add a read-only struct tm valued property using a getter function. + * This function will add a property of type 'struct tm'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_tm(ObjectClass *klass, const char *name, void (*get)(Object *, struct tm *, Error **)); @@ -1956,6 +2033,18 @@ ObjectProperty *object_property_add_uint8_ptr(Object *obj, const char *name, const uint8_t *v, ObjectPropertyFlags flags); +/** + * object_class_property_add_uint8_ptr: + * @klass: the object class to add a property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add an integer property in memory. This function will add a + * property of type 'uint8'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, const uint8_t *v, @@ -1977,6 +2066,18 @@ ObjectProperty *object_property_add_uint16_ptr(Object *obj, const char *name, const uint16_t *v, ObjectPropertyFlags flags); +/** + * object_class_property_add_uint16_ptr: + * @klass: the object class to add a property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add an integer property in memory. This function will add a + * property of type 'uint16'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, const uint16_t *v, @@ -1998,6 +2099,18 @@ ObjectProperty *object_property_add_uint32_ptr(Object *obj, const char *name, const uint32_t *v, ObjectPropertyFlags flags); +/** + * object_class_property_add_uint32_ptr: + * @klass: the object class to add a property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add an integer property in memory. This function will add a + * property of type 'uint32'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, const uint32_t *v, @@ -2019,6 +2132,18 @@ ObjectProperty *object_property_add_uint64_ptr(Object *obj, const char *name, const uint64_t *v, ObjectPropertyFlags flags); +/** + * object_class_property_add_uint64_ptr: + * @klass: the object class to add a property to + * @name: the name of the property + * @v: pointer to value + * @flags: bitwise-or'd ObjectPropertyFlags + * + * Add an integer property in memory. This function will add a + * property of type 'uint64'. + * + * Returns: The newly added property on success, or %NULL on failure. + */ ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, const uint64_t *v, @@ -2075,6 +2200,17 @@ ObjectProperty *object_property_add_const_link(Object *obj, const char *name, */ void object_property_set_description(Object *obj, const char *name, const char *description); + +/** + * object_class_property_set_description: + * @klass: the object class owning the property + * @name: the name of the property + * @description: the description of the property on the object + * + * Set an object property's description. + * + * Returns: %true on success, %false on failure. + */ void object_class_property_set_description(ObjectClass *klass, const char *name, const char *description); From 6b97f3d4d6a5c3017f6f7655019097c777f5eb86 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 17 Jul 2026 14:51:46 +0100 Subject: [PATCH 492/860] qom/object.c: rename object_class_property_uint*_ptr() to object_class_static_property_uint*_ptr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This more accurately reflects that these properties are held within the class and not the object. Update the documentation to describe the few cases where static properties should be used. Signed-off-by: Mark Cave-Ayland Reviewed-by: Daniel P. Berrangé Message-Id: <20260717135254.508701-7-mark.caveayland@nutanix.com> --- hw/riscv/spike.c | 5 ++-- include/qom/object.h | 56 +++++++++++++++++++++++++++++++------------- qom/object.c | 28 ++++++++++++---------- 3 files changed, 59 insertions(+), 30 deletions(-) diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 9fde0faf39..630b65f569 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -278,8 +278,9 @@ static void spike_machine_class_init(ObjectClass *oc, const void *data) object_class_property_add_str(oc, "signature", NULL, spike_set_signature); object_class_property_set_description(oc, "signature", "File to write ACT test signature"); - object_class_property_add_uint8_ptr(oc, "signature-granularity", - &line_size, OBJ_PROP_FLAG_WRITE); + object_class_static_property_add_uint8_ptr(oc, "signature-granularity", + &line_size, + OBJ_PROP_FLAG_WRITE); object_class_property_set_description(oc, "signature-granularity", "Size of each line in ACT signature " "file"); diff --git a/include/qom/object.h b/include/qom/object.h index 272c41cac7..11b1c9d2dc 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -2034,18 +2034,24 @@ ObjectProperty *object_property_add_uint8_ptr(Object *obj, const char *name, ObjectPropertyFlags flags); /** - * object_class_property_add_uint8_ptr: - * @klass: the object class to add a property to + * object_class_static_property_add_uint8_ptr: + * @klass: the object class to add a static property to * @name: the name of the property * @v: pointer to value * @flags: bitwise-or'd ObjectPropertyFlags * - * Add an integer property in memory. This function will add a + * Add a static integer property in memory. This function will add a * property of type 'uint8'. * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * * Returns: The newly added property on success, or %NULL on failure. */ -ObjectProperty *object_class_property_add_uint8_ptr(ObjectClass *klass, +ObjectProperty *object_class_static_property_add_uint8_ptr(ObjectClass *klass, const char *name, const uint8_t *v, ObjectPropertyFlags flags); @@ -2067,18 +2073,24 @@ ObjectProperty *object_property_add_uint16_ptr(Object *obj, const char *name, ObjectPropertyFlags flags); /** - * object_class_property_add_uint16_ptr: - * @klass: the object class to add a property to + * object_class_static_property_add_uint16_ptr: + * @klass: the object class to add a static property to * @name: the name of the property * @v: pointer to value * @flags: bitwise-or'd ObjectPropertyFlags * - * Add an integer property in memory. This function will add a + * Add a static integer property in memory. This function will add a * property of type 'uint16'. * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * * Returns: The newly added property on success, or %NULL on failure. */ -ObjectProperty *object_class_property_add_uint16_ptr(ObjectClass *klass, +ObjectProperty *object_class_static_property_add_uint16_ptr(ObjectClass *klass, const char *name, const uint16_t *v, ObjectPropertyFlags flags); @@ -2100,18 +2112,24 @@ ObjectProperty *object_property_add_uint32_ptr(Object *obj, const char *name, ObjectPropertyFlags flags); /** - * object_class_property_add_uint32_ptr: - * @klass: the object class to add a property to + * object_class_static_property_add_uint32_ptr: + * @klass: the object class to add a static property to * @name: the name of the property * @v: pointer to value * @flags: bitwise-or'd ObjectPropertyFlags * - * Add an integer property in memory. This function will add a + * Add a static integer property in memory. This function will add a * property of type 'uint32'. * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * * Returns: The newly added property on success, or %NULL on failure. */ -ObjectProperty *object_class_property_add_uint32_ptr(ObjectClass *klass, +ObjectProperty *object_class_static_property_add_uint32_ptr(ObjectClass *klass, const char *name, const uint32_t *v, ObjectPropertyFlags flags); @@ -2133,18 +2151,24 @@ ObjectProperty *object_property_add_uint64_ptr(Object *obj, const char *name, ObjectPropertyFlags flags); /** - * object_class_property_add_uint64_ptr: - * @klass: the object class to add a property to + * object_class_static_property_add_uint64_ptr: + * @klass: the object class to add a static property to * @name: the name of the property * @v: pointer to value * @flags: bitwise-or'd ObjectPropertyFlags * - * Add an integer property in memory. This function will add a + * Add a static integer property in memory. This function will add a * property of type 'uint64'. * + * A static property is one which is stored outside of the object instance, + * typically in global variables. It is only appropriate to use static + * properties when the class is designed as a singleton. If there is a + * possibility of multiple instances, then properties must be stored + * per-instance. + * * Returns: The newly added property on success, or %NULL on failure. */ -ObjectProperty *object_class_property_add_uint64_ptr(ObjectClass *klass, +ObjectProperty *object_class_static_property_add_uint64_ptr(ObjectClass *klass, const char *name, const uint64_t *v, ObjectPropertyFlags flags); diff --git a/qom/object.c b/qom/object.c index 622840f9f8..47977b1f44 100644 --- a/qom/object.c +++ b/qom/object.c @@ -2724,9 +2724,10 @@ object_property_add_uint8_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint8_ptr(ObjectClass *klass, const char *name, - const uint8_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint8_ptr(ObjectClass *klass, + const char *name, + const uint8_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; @@ -2764,9 +2765,10 @@ object_property_add_uint16_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint16_ptr(ObjectClass *klass, const char *name, - const uint16_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint16_ptr(ObjectClass *klass, + const char *name, + const uint16_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; @@ -2804,9 +2806,10 @@ object_property_add_uint32_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint32_ptr(ObjectClass *klass, const char *name, - const uint32_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint32_ptr(ObjectClass *klass, + const char *name, + const uint32_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; @@ -2844,9 +2847,10 @@ object_property_add_uint64_ptr(Object *obj, const char *name, } ObjectProperty * -object_class_property_add_uint64_ptr(ObjectClass *klass, const char *name, - const uint64_t *v, - ObjectPropertyFlags flags) +object_class_static_property_add_uint64_ptr(ObjectClass *klass, + const char *name, + const uint64_t *v, + ObjectPropertyFlags flags) { ObjectPropertyAccessor *getter = NULL; ObjectPropertyAccessor *setter = NULL; From a2deb54e7efc94ddb609c66f90bcaed5f9fbf6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 3 Feb 2023 10:55:51 +0100 Subject: [PATCH 493/860] hw/qdev: Remove DEFINE_PROP_DMAADDR() and 'hw/qdev-dma.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEFINE_PROP_DMAADDR() is only used once. Since it doesn't add much value, simply remove it, along with the header defining it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-Id: <20230203145536.17585-14-philmd@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-ohci-pci.c | 1 - hw/usb/hcd-ohci-sysbus.c | 3 +-- hw/usb/hcd-ohci.c | 1 - include/hw/core/qdev-dma.h | 16 ---------------- 4 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 include/hw/core/qdev-dma.h diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index 18b58f5fcb..70c9e9ac4f 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -25,7 +25,6 @@ #include "migration/vmstate.h" #include "hw/pci/pci_device.h" #include "hw/core/sysbus.h" -#include "hw/core/qdev-dma.h" #include "hw/core/qdev-properties.h" #include "trace.h" #include "hcd-ohci.h" diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c index 1a2cf29bed..4f51eebccb 100644 --- a/hw/usb/hcd-ohci-sysbus.c +++ b/hw/usb/hcd-ohci-sysbus.c @@ -26,7 +26,6 @@ #include "hw/usb/usb.h" #include "migration/vmstate.h" #include "hw/core/sysbus.h" -#include "hw/core/qdev-dma.h" #include "hw/core/qdev-properties.h" #include "trace.h" #include "hcd-ohci.h" @@ -61,7 +60,7 @@ static const Property ohci_sysbus_properties[] = { DEFINE_PROP_STRING("masterbus", OHCISysBusState, masterbus), DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), DEFINE_PROP_UINT32("firstport", OHCISysBusState, firstport, 0), - DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 0), + DEFINE_PROP_UINT64("dma-offset", OHCISysBusState, dma_offset, 0), }; static void ohci_sysbus_class_init(ObjectClass *klass, const void *data) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index c4a9741bd4..23990115ab 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -34,7 +34,6 @@ #include "hw/usb/usb.h" #include "migration/vmstate.h" #include "hw/core/sysbus.h" -#include "hw/core/qdev-dma.h" #include "hw/core/qdev-properties.h" #include "trace.h" #include "hcd-ohci.h" diff --git a/include/hw/core/qdev-dma.h b/include/hw/core/qdev-dma.h deleted file mode 100644 index b00391aa0c..0000000000 --- a/include/hw/core/qdev-dma.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Support for dma_addr_t typed properties - * - * Copyright (C) 2012 David Gibson, IBM Corporation. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -#ifndef HW_QDEV_DMA_H -#define HW_QDEV_DMA_H - -#define DEFINE_PROP_DMAADDR(_n, _s, _f, _d) \ - DEFINE_PROP_UINT64(_n, _s, _f, _d) - -#endif From d6c6d5500c998d3db83b334c95a3efb4eef905ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 16 Jun 2026 00:37:56 +0400 Subject: [PATCH 494/860] hw/mem/nvdimm: fix "size" property typename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The getter/setter use visit_type_size(). Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260616-qom-qapi-v2-30-cc9396b9c18c@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/mem/nvdimm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index b703252527..cf8a4d8c5f 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -99,7 +99,7 @@ static void nvdimm_set_uuid(Object *obj, Visitor *v, const char *name, static void nvdimm_init(Object *obj) { - object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", + object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "size", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL); From f9aea61a19ee54be962daafdd10da2ea8f5f1aa8 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:52 +0900 Subject: [PATCH 495/860] qdev: Make qdev_is_realized() take a const DeviceState * MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qdev_is_realized() only reads DeviceState. Make its parameter const so a later caller with a const DeviceState * can use it. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-2-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- include/hw/core/qdev.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/core/qdev.h b/include/hw/core/qdev.h index e147622341..37f7d33551 100644 --- a/include/hw/core/qdev.h +++ b/include/hw/core/qdev.h @@ -437,7 +437,7 @@ DeviceState *qdev_try_new(const char *name); * Context: May be called outside big qemu lock. * Return: true if the device has been fully constructed, false otherwise. */ -static inline bool qdev_is_realized(DeviceState *dev) +static inline bool qdev_is_realized(const DeviceState *dev) { return qatomic_load_acquire(&dev->realized); } From 1fbfec36db010b43fa36dba4c90db48752965eaa Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:53 +0900 Subject: [PATCH 496/860] hw/hyperv/balloon: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-3-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/hyperv/hv-balloon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 9dd759f11e..b8664a246b 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -197,7 +197,7 @@ static int build_dimm_list(Object *obj, void *opaque) if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized DIMMs matter */ + if (qdev_is_realized(dev)) { /* only realized DIMMs matter */ *list = g_slist_prepend(*list, dev); } } From b341bc3be85c06af6de56153d59381dcaf2e5b2e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:54 +0900 Subject: [PATCH 497/860] hw/intc/apic: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-4-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/intc/apic_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 49c03a5bce..0f0f37d457 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -424,7 +424,7 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name, DeviceState *dev = DEVICE(obj); uint32_t value; - if (dev->realized) { + if (qdev_is_realized(dev)) { qdev_prop_set_after_realize(dev, name, errp); return; } From 9cc59476bbbf4b4b32ad32fe4b396059d7a7cdf6 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:55 +0900 Subject: [PATCH 498/860] hw/mem/memory-device: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-5-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/mem/memory-device.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 9cf82b84cf..24bebfb6b3 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -59,7 +59,7 @@ static int memory_device_build_list(Object *obj, void *opaque) if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized memory devices matter */ + if (qdev_is_realized(dev)) { *list = g_slist_insert_sorted(*list, dev, memory_device_addr_sort); } } @@ -326,7 +326,7 @@ static int memory_device_plugged_size(Object *obj, void *opaque) const MemoryDeviceState *md = MEMORY_DEVICE(obj); const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); - if (dev->realized && !memory_device_is_empty(md)) { + if (qdev_is_realized(dev) && !memory_device_is_empty(md)) { *size += mdc->get_plugged_size(md, &error_abort); } } From 4ef1d02325e131bde790a710925b974378e7072f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:56 +0900 Subject: [PATCH 499/860] hw/mem/pc-dimm: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-6-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/mem/pc-dimm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 3efe47f499..68862926ee 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -103,7 +103,7 @@ static int pc_dimm_slot2bitmap(Object *obj, void *opaque) if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* count only realized DIMMs */ + if (qdev_is_realized(dev)) { /* count only realized DIMMs */ PCDIMMDevice *d = PC_DIMM(obj); set_bit(d->slot, bitmap); } From 56258e616f0e45bed3e1346f32c7472f7d550d2e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:57 +0900 Subject: [PATCH 500/860] hw/nvram: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-7-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/nvram/xlnx-bbram.c | 2 +- hw/nvram/xlnx-efuse.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index edfb592a5e..e336874bde 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -468,7 +468,7 @@ static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, qdev_prop_drive.set(obj, v, name, opaque, errp); /* Fill initial data if backend is attached after realized */ - if (dev->realized) { + if (qdev_is_realized(dev)) { bbram_bdrv_read(XLNX_BBRAM(obj), errp); } } diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index 1a9650ba12..03c9fbc0d0 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -233,7 +233,7 @@ static void efuse_prop_set_drive(Object *obj, Visitor *v, const char *name, qdev_prop_drive.set(obj, v, name, opaque, errp); /* Fill initial data if backend is attached after realized */ - if (dev->realized) { + if (qdev_is_realized(dev)) { efuse_bdrv_read(XLNX_EFUSE(obj), errp); } } From f80ce4a6541b406a896410af12690b2b3148badc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:58 +0900 Subject: [PATCH 501/860] hw/ppc/pnv_xscom: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-8-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/ppc/pnv_xscom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index dc1ffc6c01..7af722584e 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -253,7 +253,7 @@ static int xscom_dt_child(Object *child, void *opaque) /* * Only "realized" devices should be configured in the DT */ - if (xc->dt_xscom && DEVICE(child)->realized) { + if (xc->dt_xscom && qdev_is_realized(DEVICE(child))) { _FDT((xc->dt_xscom(xd, args->fdt, args->xscom_offset))); } } From 30068ef7467ef51becbb4dce946bdf917f0f0981 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:16:59 +0900 Subject: [PATCH 502/860] hw/vfio: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-9-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/vfio/container-legacy.c | 4 ++-- hw/vfio/device.c | 4 ++-- hw/vfio/iommufd.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index d301b27aa6..7ac7b37214 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -1075,7 +1075,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 || + if (!qdev_is_realized(vbasedev_iter->dev) || !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } @@ -1160,7 +1160,7 @@ out: } QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { - if (!vbasedev_iter->dev->realized || + if (!qdev_is_realized(vbasedev_iter->dev) || !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 1a7f8088aa..4f11959633 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -59,13 +59,13 @@ void vfio_device_reset_handler(void *opaque) trace_vfio_device_reset_handler(); QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->dev->realized) { + if (qdev_is_realized(vbasedev->dev)) { vbasedev->ops->vfio_compute_needs_reset(vbasedev); } } QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->dev->realized && vbasedev->needs_reset) { + if (qdev_is_realized(vbasedev->dev) && vbasedev->needs_reset) { vbasedev->ops->vfio_hot_reset_multi(vbasedev); } } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 6ff668d259..242644aa00 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -829,7 +829,7 @@ 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 (!vfio_pci_from_vfio_device(vbasedev_tmp) || - !vbasedev_tmp->dev->realized) { + !qdev_is_realized(vbasedev_tmp->dev)) { return NULL; } From a3413cc176a7d453b0a75791df99b67b7027355f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:17:00 +0900 Subject: [PATCH 503/860] hw/virtio/virtio-mem: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-10-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/virtio/virtio-mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 35e03ed759..7130ed852d 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1453,7 +1453,7 @@ static void virtio_mem_set_requested_size(Object *obj, Visitor *v, * The block size and memory backend are not fixed until the device was * realized. realize() will verify these properties then. */ - if (DEVICE(obj)->realized) { + if (qdev_is_realized(DEVICE(obj))) { if (!QEMU_IS_ALIGNED(value, vmem->block_size)) { error_setg(errp, "'%s' has to be multiples of '%s' (0x%" PRIx64 ")", name, VIRTIO_MEM_BLOCK_SIZE_PROP, @@ -1507,7 +1507,7 @@ static void virtio_mem_set_block_size(Object *obj, Visitor *v, const char *name, VirtIOMEM *vmem = VIRTIO_MEM(obj); uint64_t value; - if (DEVICE(obj)->realized) { + if (qdev_is_realized(DEVICE(obj))) { error_setg(errp, "'%s' cannot be changed", name); return; } From 2c69525a8d70117748353341a64093a96faaa467 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:17:01 +0900 Subject: [PATCH 504/860] hw/virtio/virtio-qmp: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-11-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/virtio/virtio-qmp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio-qmp.c b/hw/virtio/virtio-qmp.c index 57cc03828c..5aaeebdd96 100644 --- a/hw/virtio/virtio-qmp.c +++ b/hw/virtio/virtio-qmp.c @@ -659,7 +659,7 @@ static int query_dev_child(Object *child, void *opaque) { VirtioInfoList **vdevs = opaque; Object *dev = object_dynamic_cast(child, TYPE_VIRTIO_DEVICE); - if (dev != NULL && DEVICE(dev)->realized) { + if (dev != NULL && qdev_is_realized(DEVICE(dev))) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtioInfo *info = g_new(VirtioInfo, 1); @@ -688,7 +688,7 @@ VirtIODevice *qmp_find_virtio_device(const char *path) /* Verify the canonical path is a realized virtio device */ Object *dev = object_dynamic_cast(object_resolve_path(path, NULL), TYPE_VIRTIO_DEVICE); - if (!dev || !DEVICE(dev)->realized) { + if (!dev || !qdev_is_realized(DEVICE(dev))) { return NULL; } return VIRTIO_DEVICE(dev); From 58c4aeacfa5d4e911b1b63f738295f7b637da9d4 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:17:02 +0900 Subject: [PATCH 505/860] target/i386/cpu: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-12-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- target/i386/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5805d33ab9..7a2c3cf358 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -10387,7 +10387,7 @@ static void x86_cpu_set_bit_prop(Object *obj, Visitor *v, const char *name, BitProperty *fp = opaque; bool value; - if (dev->realized) { + if (qdev_is_realized(dev)) { qdev_prop_set_after_realize(dev, name, errp); return; } From 72422beb01001241e8425677e455c4a4c8274549 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:17:03 +0900 Subject: [PATCH 506/860] target/s390x: Use qdev_is_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceState fields should be accessed through qdev helpers rather than directly. Use qdev_is_realized() instead of reading DeviceState::realized directly. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-13-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- target/s390x/cpu_models.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 2dd9aac807..f292af7ad0 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -714,7 +714,7 @@ static void set_feature(Object *obj, Visitor *v, const char *name, S390CPU *cpu = S390_CPU(obj); bool value; - if (dev->realized) { + if (qdev_is_realized(dev)) { error_setg(errp, "Attempt to set property '%s' on '%s' after " "it was realized", name, object_get_typename(obj)); return; @@ -770,7 +770,7 @@ static void set_feature_group(Object *obj, Visitor *v, const char *name, S390CPU *cpu = S390_CPU(obj); bool value; - if (dev->realized) { + if (qdev_is_realized(dev)) { error_setg(errp, "Attempt to set property '%s' on '%s' after " "it was realized", name, object_get_typename(obj)); return; From c38575a1265cb219230e75ede8e1750b6a269709 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 21 Jul 2026 17:17:04 +0900 Subject: [PATCH 507/860] hw/qdev: Parent device before setting parent bus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9940b2cfbc05 ("qdev: New qdev_new(), qdev_realize(), etc.") says "device state 'no QOM parent, but plugged into bus' is dangerous". In such a case, unrealizing the bus will hang in bus_unparent(): while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { DeviceState *dev = kid->child; object_unparent(OBJECT(dev)); } object_unparent() does nothing when its argument has no QOM parent, and the loop spins forever. However, that commit did not completely eliminate such a situation. When the device is not parented, device_set_realized() lets /machine/unattached parent it, but it happens after setting parent bus. Therefore, any failure between the two operations can leave the device in a dangerous state. qdev_realize() at least asserts that the device is not already realized and prevents one realization failure pattern, but it is not comprehensive. Besides, it will trip with a command line like the following: qemu-system-x86_64 -M none -nodefaults -nographic \ -device ipmi-bmc-sim,realized=on Eliminate the dangerous state by ensuring that the device is parented before calling qdev_set_parent_bus(). Also, stop asserting that the device is not already realized in qdev_realize(); it is broken and no longer serves any purpose. Fixes: 9940b2cfbc05 ("qdev: New qdev_new(), qdev_realize(), etc.") Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20260721-qdev-v3-14-d2e226fa002e@rsg.ci.i.u-tokyo.ac.jp> --- hw/core/qdev.c | 51 ++++++++++++++++++++++++------------------ tests/unit/test-qdev.c | 13 +++++++++++ 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index e2aab3d1fc..0b0f2f47fa 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -264,17 +264,43 @@ static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb, bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp) { - assert(!dev->realized && !dev->parent_bus); + static int unattached_count; + bool unattached_parent = false; + + assert(!dev->parent_bus); + + if (!OBJECT(dev)->parent) { + gchar *name = g_strdup_printf("device[%d]", unattached_count++); + + object_property_add_child(machine_get_container("unattached"), + name, OBJECT(dev)); + unattached_parent = true; + g_free(name); + } if (bus) { if (!qdev_set_parent_bus(dev, bus, errp)) { - return false; + goto fail; } } else { assert(!DEVICE_GET_CLASS(dev)->bus_type); } - return object_property_set_bool(OBJECT(dev), "realized", true, errp); + if (object_property_set_bool(OBJECT(dev), "realized", true, errp)) { + return true; + } + +fail: + if (unattached_parent) { + /* + * Beware, this doesn't just revert + * object_property_add_child(), it also runs bus_remove()! + */ + object_unparent(OBJECT(dev)); + unattached_count--; + } + + return false; } bool qdev_realize_and_unref(DeviceState *dev, BusState *bus, Error **errp) @@ -479,8 +505,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp) BusState *bus; NamedClockList *ncl; Error *local_err = NULL; - bool unattached_parent = false; - static int unattached_count; if (dev->hotplugged && !dc->hotpluggable) { error_setg(errp, "Device '%s' does not support hotplugging", @@ -493,15 +517,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp) goto fail; } - if (!obj->parent) { - gchar *name = g_strdup_printf("device[%d]", unattached_count++); - - object_property_add_child(machine_get_container("unattached"), - name, obj); - unattached_parent = true; - g_free(name); - } - hotplug_ctrl = qdev_get_hotplug_handler(dev); if (hotplug_ctrl) { hotplug_handler_pre_plug(hotplug_ctrl, dev, &local_err); @@ -627,14 +642,6 @@ post_realize_fail: fail: error_propagate(errp, local_err); - if (unattached_parent) { - /* - * Beware, this doesn't just revert - * object_property_add_child(), it also runs bus_remove()! - */ - object_unparent(OBJECT(dev)); - unattached_count--; - } } static bool device_get_hotpluggable(Object *obj, Error **errp) diff --git a/tests/unit/test-qdev.c b/tests/unit/test-qdev.c index 20eae38e03..77c3eee717 100644 --- a/tests/unit/test-qdev.c +++ b/tests/unit/test-qdev.c @@ -78,6 +78,16 @@ static void test_qdev_free_properties(void) object_unref(mt); } +static void test_qdev_double_realization(void) +{ + MyDev *mt = STATIC_TYPE(object_new(TYPE_MY_DEV)); + + qdev_realize(DEVICE(mt), NULL, &error_fatal); + qdev_realize(DEVICE(mt), NULL, &error_fatal); + object_unparent(OBJECT(mt)); + object_unref(OBJECT(mt)); +} + int main(int argc, char **argv) { @@ -90,6 +100,9 @@ int main(int argc, char **argv) g_test_add_func("/qdev/free-properties", test_qdev_free_properties); + g_test_add_func("/qdev/double-realization", + test_qdev_double_realization); + g_test_run(); return 0; From 6ccf1e73fc65a7ad52eca1f8ecfa925a03acd987 Mon Sep 17 00:00:00 2001 From: Dario Faggioli Date: Thu, 30 Jul 2026 18:22:38 +0200 Subject: [PATCH 508/860] hw/i386/pc: xen: reinstate the "xenfv" machine alias MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 7d2778dea32a4924a469f1cc5767042c8f5b4eea ("hw/i386/pc: Remove deprecated pc-q35/pc-i440fx/xenfv 3.1 machines") removed the Xen machine type that was providing the "xenfv" alias. As a consequence, since the tools are apparently relying on such alias, we're getting this, as soon as one tries to start a Xen (HVM) VM: qemu-system-i386: unsupported machine type: "xenfv" Use -machine help to list supported machines Reinstate the alias and let it point to the only Xen machine we still have. Cc: qemu-stable@nongnu.org Fixes: 7d2778dea3 Remove deprecated pc-q35/pc-i440fx/xenfv 3.1 machines Signed-off-by: Dario Faggioli Reviewed-by: Daniel P. Berrangé Acked-by: Stefano Stabellini Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260730162238.3308286-1-dfaggioli@suse.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_piix.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 223a19cc4b..a929cc4dec 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -667,6 +667,7 @@ static void xenfv_machine_4_2_options(MachineClass *m) { pc_i440fx_machine_4_2_options(m); m->desc = "Xen Fully-virtualized PC"; + m->alias = "xenfv"; m->max_cpus = HVM_MAX_VCPUS; m->default_machine_opts = "accel=xen,suppress-vmdesc=on"; } From 59fc7b747d56652e09708adff3afaca326e85a2a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 31 Jul 2026 10:36:17 +0100 Subject: [PATCH 509/860] hw/net/rtl8139: Fix handling of VLAN tags on incoming short packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rtl8139 receive code handles VLAN tags in incoming packets by copying the VLAN tag to a special field in the receive descriptor, and copying only the actual payload data to the receive buffer. This code tries to ensure that it pads out the payload to at least MIN_BUF_SIZE bytes. In commit 63b901bfd30 we removed the main "pad short frames" code from this device because we switched to requiring net backends to do the padding. However we didn't notice that this broke the VLAN tag handling, which relied on the old code making the buffer at least MIN_BUF_SIZE + VLAN_HLEN bytes so that it could copy MIN_BUF_SIZE bytes into the receive buffer even after removing the VLAN tag. The result is that the guest can make us read 4 bytes off the end of a buffer by feeding itself a suitable short packet in loopback mode. The old behaviour is actually not correct, because the IEEE802.1Q standard says that the minimum ethernet frame size remains 64 bytes including the 4 checksum bytes, and so when a tag is present the payload data only needs to be 56 bytes. (A bridge implementation can choose to pad tagged frames out to 68 bytes, but it doesn't have to, and so all devices have to correctly handle incoming tagged frames that are 64 bytes long.) The RTL8139 datasheet isn't very communicative on this topic, but there's nothing that suggests it adds extra padding on receive that didn't exist in the incoming packet. Drop the last remnants of the padding handling from this device; this avoids overcopying into the guest when we receive a short VLAN tagged packet. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3518 Signed-off-by: Peter Maydell Reviewed-by: Bin Meng Message-ID: <20260731093618.2961031-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/rtl8139.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 424af73a18..2b61c171f2 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -778,7 +778,6 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) s->RxBufAddr += size; } -#define MIN_BUF_SIZE 60 static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high) { return low | ((uint64_t)high << 32); @@ -1007,10 +1006,6 @@ static ssize_t rtl8139_receive(NetClientState *nc, lduw_be_p(&buf[ETH_ALEN * 2]) == ETH_P_VLAN) { dot1q_buf = &buf[ETH_ALEN * 2]; size -= VLAN_HLEN; - /* if too small buffer, use the tailroom added duing expansion */ - if (size < MIN_BUF_SIZE) { - size = MIN_BUF_SIZE; - } rxdw1 &= ~CP_RX_VLAN_TAG_MASK; /* BE + ~le_to_cpu()~ + cpu_to_le() = BE */ From f3a10c1c454789ad6bb0633118019f4c6544b4b3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 31 Jul 2026 10:36:18 +0100 Subject: [PATCH 510/860] hw/net/rtl8139: Send whole of vlan-tagged packet when doing loopback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In rtl8139_transfer_frame(), if we are transmitting a frame over loopback then we do this by calling qemu_receive_packet(). If we have an iovec rather than a simple buffer (which happens only when we're sending a packet where we are inserting a vlan tag), we have to convert this into a simple buffer first using iov_to_buf(). However, when we do this we forget to also update the 'size' local variable to the size of the new simple buffer, so we will truncate the packet by 4 bytes (the size of the vlan tag). Correct the logic so we don't truncate vlan-tagged packets when sending them over loopback. Cc: qemu-stable@nongnu.org Reported-by: Bin Meng Signed-off-by: Peter Maydell Reviewed-by: Bin Meng Message-ID: <20260731093618.2961031-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/rtl8139.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 2b61c171f2..16479284ee 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -1765,6 +1765,7 @@ static void rtl8139_transfer_frame(RTL8139State *s, uint8_t *buf, int size, buf2 = g_malloc(buf2_size); iov_to_buf(iov, 3, 0, buf2, buf2_size); buf = buf2; + size = buf2_size; } DPRINTF("+++ transmit loopback mode\n"); From 60d010f66f2ad7c15388a605e6f1a2114dc60185 Mon Sep 17 00:00:00 2001 From: Bin Guo Date: Mon, 3 Aug 2026 12:18:08 +0800 Subject: [PATCH 511/860] hw/block/pflash_cfi01: Restore ROMD mode after migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pflash_post_load() did not restore the ROMD mode of the memory region. Although cmd and wcycle are migrated, the destination retains the default ROMD = true from realize. When the source was in a non-array mode (e.g. ID read, cmd = 0x90), reads on the destination bypass pflash_read() via the ROM fast path and return raw storage bytes instead of the command-specific response. Derive ROMD from the migrated cmd/wcycle in pflash_post_load. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4042 Cc: qemu-stable@nongnu.org Signed-off-by: Bin Guo Reviewed-by: Peter Maydell [PMD: Including review comments from https://lore.kernel.org/qemu-devel/CAFEAcA-P6RH7nJK0KQ1H8576ULFA7nocB0EkhhZf6Rw3g0WCag@mail.gmail.com/ Confirming that this is correct is a bit tricky. It relies on: * when we set romd mode to true we also set wcycle = 0, cmd = 0 (which we do, in reset and in the mode_read_array code) * when we set romd mode to false at the top of pflash_write(), all paths out of that function either go through the mode_read_array path, or else update pfl->cmd to something non-zero * nowhere outside pflash_write() udpates cmd or wcycle except for the "clear them to 0 and set romd mode" places This is almost but not quite true. In pflash_read(), the default case for the pfl->cmd switch sets wcycle = 0 cmd = 0 but doesn't change the romd state. Luckily the "this should never happen" comment is true -- there's no way to get a pfl->cmd that falls into the default (except for being deliberately fed a bogus value via inbound migration). ] Message-ID: <20260803041808.58174-1-guobin@linux.alibaba.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/block/pflash_cfi01.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 5b9ddb20b1..a13b91967e 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -1030,6 +1030,16 @@ static int pflash_post_load(void *opaque, int version_id) { PFlashCFI01 *pfl = opaque; + /* + * ROMD mode is not in the VMState; derive it from the migrated + * cmd and wcycle. Only (wcycle == 0, cmd == 0x00) is read-array. + */ + if (pfl->wcycle == 0 && pfl->cmd == 0x00) { + memory_region_rom_device_set_romd(&pfl->mem, true); + } else { + memory_region_rom_device_set_romd(&pfl->mem, false); + } + if (!pfl->ro) { pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, pfl); From 6dab35f5492b04083f70726a3048d89d8deb4a32 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 10 Aug 2026 17:48:46 -0400 Subject: [PATCH 512/860] hw/nvme: add SPDM_SOCKET Kconfig dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NVMe emulation code unconditionally calls spdm_socket_*() APIs. Add a Kconfig dependency to avoid build errors when NVME_PCI is enabled without SPDM_SOCKET. Fixes: 4f947b10d52 ("hw/nvme: Add SPDM over DOE support") Signed-off-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260810214846.76805-1-stefanha@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/nvme/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/nvme/Kconfig b/hw/nvme/Kconfig index cfa2ab0f9d..cfeeed64cd 100644 --- a/hw/nvme/Kconfig +++ b/hw/nvme/Kconfig @@ -2,3 +2,4 @@ config NVME_PCI bool default y if PCI_DEVICES || PCIE_DEVICES depends on PCI + select SPDM_SOCKET From 5147e585e7002a3c4dbdad3ca85c716925831541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Aug 2026 07:43:49 +0200 Subject: [PATCH 513/860] hw/cpu: Correct CPU_GET_CLASS() comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 0e86d7a71e8 ("cpus: Cache CPUClass early in instance_init() handler") renamed cpu_exec_realizefn() -> cpu_common_initfn(); update the comment. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-2-philmd@oss.qualcomm.com> --- include/hw/core/cpu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index b54035fb13..c5064a5449 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -60,7 +60,7 @@ typedef int (*WriteCoreDumpFunction)(const void *buf, size_t size, * expensive given the eventual call to * object_class_dynamic_cast_assert(). Because of this the CPUState * has a cached value for the class in cs->cc which is set up in - * cpu_exec_realizefn() for use in hot code paths. + * cpu_common_initfn() for use in hot code paths. */ typedef struct CPUClass CPUClass; DECLARE_CLASS_CHECKERS(CPUClass, CPU, From fec261633c55d6ee50d812da7c84fb2de7eb60d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Aug 2026 16:46:15 +0200 Subject: [PATCH 514/860] hw/cpu: Include missing 'qemu/accel.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_common_realize() calls accel_cpu_common_realize(), itself declared in "qemu/accel.h". Include the latter, otherwise we get when refactoring unrelated headers: hw/core/cpu-common.c:233:10: error: implicit declaration of function ‘accel_cpu_common_realize’ 233 | if (!accel_cpu_common_realize(cpu, errp)) { | ^~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-3-philmd@oss.qualcomm.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/cpu-common.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index e314f916f8..8c9ff25e04 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -23,6 +23,7 @@ #include "qapi/error.h" #include "hw/core/cpu.h" #include "system/hw_accel.h" +#include "qemu/accel.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/lockcnt.h" From a2d626371dfc08e63009bbc73a9f3e5ae37b63ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Aug 2026 08:00:49 +0200 Subject: [PATCH 515/860] hw/cpu: Move internal declarations to new 'cpu-internal.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some declarations are only used within hw/core/, in particular by the 3 cpu-{common,user,system}.c. Restrict the declarations scope by moving them to a new "cpu-internal.h" local header. Rename cpu_exec_initfn() -> cpu_exec_init() because we usually have the 'fn' suffix for handler, not API entry point methods. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-4-philmd@oss.qualcomm.com> --- MAINTAINERS | 1 + hw/core/cpu-common.c | 3 ++- hw/core/cpu-internal.h | 22 ++++++++++++++++++++++ hw/core/cpu-system.c | 3 ++- hw/core/cpu-user.c | 3 ++- include/hw/core/cpu.h | 6 ------ 6 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 hw/core/cpu-internal.h diff --git a/MAINTAINERS b/MAINTAINERS index f249be744e..c296dd5abf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2093,6 +2093,7 @@ Machine core M: Philippe Mathieu-Daudé R: Zhao Liu S: Maintained +F: hw/core/cpu-internal.h F: hw/core/cpu-common.c F: hw/core/cpu-system.c F: hw/core/machine-qmp-cmds.c diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 8c9ff25e04..17d7658093 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -39,6 +39,7 @@ #ifdef CONFIG_PLUGIN #include "qemu/plugin.h" #endif +#include "cpu-internal.h" CPUState *cpu_by_arch_id(int64_t id) { @@ -326,7 +327,7 @@ static void cpu_common_initfn(Object *obj) QTAILQ_INIT(&cpu->breakpoints); QTAILQ_INIT(&cpu->watchpoints); - cpu_exec_initfn(cpu); + cpu_exec_init(cpu); /* * Plugin initialization must wait until the cpu start executing diff --git a/hw/core/cpu-internal.h b/hw/core/cpu-internal.h new file mode 100644 index 0000000000..ae6a31bca2 --- /dev/null +++ b/hw/core/cpu-internal.h @@ -0,0 +1,22 @@ +/* + * QEMU private CPU interface between user / system modes) + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_CORE_CPU_INTERNAL_H +#define HW_CORE_CPU_INTERNAL_H + +#include "hw/core/qdev.h" +#include "hw/core/cpu.h" + +void cpu_class_init_props(DeviceClass *dc); +void cpu_exec_class_post_init(CPUClass *cc); + +void cpu_exec_init(CPUState *cpu); + +void cpu_vmstate_register(CPUState *cpu); +void cpu_vmstate_unregister(CPUState *cpu); + +#endif diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 14eb4ed87f..8a9f2fcea8 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -30,6 +30,7 @@ #include "hw/core/sysemu-cpu-ops.h" #include "migration/vmstate.h" #include "system/tcg.h" +#include "cpu-internal.h" bool cpu_has_work(CPUState *cpu) { @@ -188,7 +189,7 @@ void cpu_exec_class_post_init(CPUClass *cc) g_assert(cc->sysemu_ops->has_work); } -void cpu_exec_initfn(CPUState *cpu) +void cpu_exec_init(CPUState *cpu) { cpu->memory = get_system_memory(); object_ref(OBJECT(cpu->memory)); diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c index 25aa25ad24..c2cb00a85d 100644 --- a/hw/core/cpu-user.c +++ b/hw/core/cpu-user.c @@ -11,6 +11,7 @@ #include "hw/core/qdev-properties.h" #include "hw/core/cpu.h" #include "migration/vmstate.h" +#include "cpu-internal.h" static const Property cpu_user_props[] = { /* @@ -32,7 +33,7 @@ void cpu_exec_class_post_init(CPUClass *cc) /* nothing to do */ } -void cpu_exec_initfn(CPUState *cpu) +void cpu_exec_init(CPUState *cpu) { /* nothing to do */ } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index c5064a5449..6490fb070f 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1178,12 +1178,6 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) */ 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); -void cpu_exec_initfn(CPUState *cpu); -void cpu_vmstate_register(CPUState *cpu); -void cpu_vmstate_unregister(CPUState *cpu); bool cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); void cpu_exec_reset_hold(CPUState *cpu); From dc6c2e66727fbd8a670ba821f75f82e80d7b9216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Aug 2026 16:34:57 +0200 Subject: [PATCH 516/860] hw/cpu: Rename cpu_common_realizefn() -> cpu_exec_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep cpu_common_*() pattern for publicly exposed common methods used by target code. Use cpu_exec_*() pattern for internal ones, mostly to distinct between system / user mode. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-5-philmd@oss.qualcomm.com> --- hw/core/cpu-common.c | 4 ++-- target/i386/kvm/kvm-cpu.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 17d7658093..33e349d440 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -246,7 +246,7 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp) return true; } -static void cpu_common_realizefn(DeviceState *dev, Error **errp) +static void cpu_exec_realize(DeviceState *dev, Error **errp) { CPUState *cpu = CPU(dev); Object *machine = qdev_get_machine(); @@ -379,7 +379,7 @@ static void cpu_common_class_init(ObjectClass *klass, const void *data) k->gdb_read_register = cpu_common_gdb_read_register; k->gdb_write_register = cpu_common_gdb_write_register; set_bit(DEVICE_CATEGORY_CPU, dc->categories); - dc->realize = cpu_common_realizefn; + dc->realize = cpu_exec_realize; dc->unrealize = cpu_common_unrealizefn; rc->phases.hold = cpu_common_reset_hold; rc->phases.exit = cpu_common_reset_exit; diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index c34d9f15c7..e164cef203 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -63,7 +63,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * host_cpu_realizefn() * kvm_set_guest_phys_bits() * check/update ucode_rev, phys_bits, guest_phys_bits, mwait - * cpu_common_realizefn() (via xcc->parent_realize) + * cpu_exec_realize() (via xcc->parent_realize) */ if (xcc->max_features) { if (enable_cpu_pm) { From abf1023d639ccc19f452e92e40a8b3e90e222958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Aug 2026 07:55:51 +0200 Subject: [PATCH 517/860] hw/cpu: Rename cpu_exec_realizefn() -> cpu_common_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep cpu_common_*() pattern for publicly exposed common methods used by target code. Use cpu_exec_*() pattern for internal ones, mostly to distinct between system / user mode. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-6-philmd@oss.qualcomm.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/cpu-common.c | 2 +- include/hw/core/cpu.h | 3 ++- target/alpha/cpu.c | 2 +- target/arm/cpu.c | 2 +- target/avr/cpu.c | 2 +- target/hexagon/cpu.c | 2 +- target/hppa/cpu.c | 2 +- target/i386/cpu.c | 10 +++++----- target/i386/kvm/kvm-cpu.c | 2 +- target/loongarch/cpu.c | 2 +- target/m68k/cpu.c | 2 +- target/microblaze/cpu.c | 2 +- target/mips/cpu.c | 2 +- target/or1k/cpu.c | 2 +- target/ppc/cpu_init.c | 2 +- target/riscv/cpu.c | 2 +- target/riscv/kvm/kvm-cpu.c | 2 +- target/riscv/tcg/tcg-cpu.c | 2 +- target/rx/cpu.c | 2 +- target/s390x/cpu.c | 2 +- target/sh4/cpu.c | 2 +- target/sparc/cpu.c | 2 +- target/tricore/cpu.c | 2 +- target/xtensa/cpu.c | 2 +- 24 files changed, 29 insertions(+), 28 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 33e349d440..732e32fade 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -230,7 +230,7 @@ const char *parse_cpu_option(const char *cpu_option) return cpu_type; } -bool cpu_exec_realizefn(CPUState *cpu, Error **errp) +bool cpu_common_realize(CPUState *cpu, Error **errp) { if (!accel_cpu_common_realize(cpu, errp)) { return false; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 6490fb070f..7b7cf330cd 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1178,7 +1178,8 @@ G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) */ void qemu_process_cpu_events(CPUState *cpu); -bool cpu_exec_realizefn(CPUState *cpu, Error **errp); +/** cpu_common_realize: CPU DeviceRealize common handler */ +bool cpu_common_realize(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); void cpu_exec_reset_hold(CPUState *cpu); diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 0c35067b20..12e8602166 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -117,7 +117,7 @@ static void alpha_cpu_realizefn(DeviceState *dev, Error **errp) cs->tcg_cflags |= CF_PCREL; #endif - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 787e4dc7ab..77aa78f00e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1891,7 +1891,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } #endif - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 3591219212..f8409f32ab 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -131,7 +131,7 @@ static void avr_cpu_realizefn(DeviceState *dev, Error **errp) AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 0ddd898e6e..7067e5b70f 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -457,7 +457,7 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index a68152f968..07b49e5132 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -181,7 +181,7 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) HPPACPUClass *acc = HPPA_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 7a2c3cf358..e5ffb10d15 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -10164,7 +10164,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * These may be set by the accel-specific code, * and the results are subsequently checked / assumed in this function. */ - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; @@ -10179,7 +10179,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (cpu->guest_phys_bits == -1) { /* * If it was not set by the user, or by the accelerator via - * cpu_exec_realizefn, clear. + * cpu_common_realize, clear. */ cpu->guest_phys_bits = 0; } @@ -10188,7 +10188,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) /* * The default is the same as KVM's. Note that this check * needs to happen after the evenual setting of ucode_rev in - * accel-specific code in cpu_exec_realizefn. + * accel-specific code in cpu_common_realize. */ if (IS_AMD_CPU(env)) { cpu->ucode_rev = 0x01000065; @@ -10201,7 +10201,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * mwait extended info: needed for Core compatibility * We always wake on interrupt even if host does not have the capability. * - * requires the accel-specific code in cpu_exec_realizefn to + * requires the accel-specific code in cpu_common_realize to * have already acquired the CPUID data into cpu->mwait. */ cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; @@ -10230,7 +10230,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * Note that this code assumes features expansion has already been done * (as it checks for CPUID_EXT2_LM), and also assumes that potential * phys_bits adjustments to match the host have been already done in - * accel-specific code in cpu_exec_realizefn. + * accel-specific code in cpu_common_realize. */ if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { if (cpu->phys_bits && cpu->phys_bits < 32) { diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index e164cef203..da8d77ade0 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -57,7 +57,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * * x86_cpu_realizefn(): * x86_cpu_expand_features() - * cpu_exec_realizefn(): + * cpu_common_realize(): * accel_cpu_common_realize() * kvm_cpu_realizefn() * host_cpu_realizefn() diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index fb03424ffa..cb07f15110 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -704,7 +704,7 @@ static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 9ad3057d91..ce2707dee5 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -389,7 +389,7 @@ static void m68k_cpu_realizefn(DeviceState *dev, Error **errp) register_m68k_insns(&cpu->env); - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index a97c92a7b6..389a5124b1 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -251,7 +251,7 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) int i = 0; Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/mips/cpu.c b/target/mips/cpu.c index d72044aef6..669c7d99bb 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -544,7 +544,7 @@ static void mips_cpu_realizefn(DeviceState *dev, Error **errp) } mips_cp0_period_set(cpu); - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/or1k/cpu.c b/target/or1k/cpu.c index ea29b2e01f..66c00c0930 100644 --- a/target/or1k/cpu.c +++ b/target/or1k/cpu.c @@ -170,7 +170,7 @@ static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index f404c7e549..b07e9c3ff9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6921,7 +6921,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5a82e6563b..23b5023dd3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1226,7 +1226,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 495cb42dc8..97069bf597 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -2006,7 +2006,7 @@ static void kvm_cpu_instance_init(CPUState *cs) * We'll get here via the following path: * * riscv_cpu_realize() - * -> cpu_exec_realizefn() + * -> cpu_common_realize() * -> kvm_cpu_realize() (via accel_cpu_common_realize()) */ static bool kvm_cpu_realize(CPUState *cs, Error **errp) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 4af5cd9c73..9e3cc87f8a 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1304,7 +1304,7 @@ static void riscv_cpu_set_profile(RISCVCPU *cpu, * We'll get here via the following path: * * riscv_cpu_realize() - * -> cpu_exec_realizefn() + * -> cpu_common_realize() * -> tcg_cpu_realize() (via accel_cpu_common_realize()) */ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 20b188c24c..9b8473d71c 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -147,7 +147,7 @@ static void rx_cpu_realize(DeviceState *dev, Error **errp) RXCPUClass *rcc = RX_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index c074e12ba2..641ea96c8e 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -249,7 +249,7 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) } #endif - cpu_exec_realizefn(cs, &err); + cpu_common_realize(cs, &err); if (err != NULL) { goto out; } diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 763424695c..ad2ec28c1b 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -250,7 +250,7 @@ static void superh_cpu_realizefn(DeviceState *dev, Error **errp) SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(dev); Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 13ebb122a3..1bc14b586b 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -892,7 +892,7 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) /* Default NaN value: sign bit clear, all frac bits set */ set_float_default_nan_pattern(0b01111111, &env->fp_status); - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 472c24ae32..96e2817dee 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -99,7 +99,7 @@ static void tricore_cpu_realizefn(DeviceState *dev, Error **errp) CPUTriCoreState *env = &cpu->env; Error *local_err = NULL; - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 2015a4e3d0..7c25b9ab70 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -257,7 +257,7 @@ static void xtensa_cpu_realizefn(DeviceState *dev, Error **errp) xtensa_irq_init(&XTENSA_CPU(dev)->env); #endif - cpu_exec_realizefn(cs, &local_err); + cpu_common_realize(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); return; From 5293cecb8921aef335471d3649013567daaa0378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Aug 2026 08:11:46 +0200 Subject: [PATCH 518/860] hw/cpu: Rename cpu_exec_unrealizefn() -> cpu_common_unrealize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep cpu_common_*() pattern for publicly exposed common methods used by target code. Use cpu_exec_*() pattern for internal ones, mostly to distinct between system / user mode. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-7-philmd@oss.qualcomm.com> --- hw/core/cpu-common.c | 4 ++-- include/hw/core/cpu.h | 4 +++- target/ppc/cpu_init.c | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 732e32fade..59cd489b71 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -284,10 +284,10 @@ static void cpu_common_unrealizefn(DeviceState *dev) #endif /* NOTE: latest generic point before the cpu is fully unrealized */ - cpu_exec_unrealizefn(cpu); + cpu_common_unrealize(cpu); } -void cpu_exec_unrealizefn(CPUState *cpu) +void cpu_common_unrealize(CPUState *cpu) { cpu_vmstate_unregister(cpu); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 7b7cf330cd..81af7b9ee1 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1180,7 +1180,9 @@ void qemu_process_cpu_events(CPUState *cpu); /** cpu_common_realize: CPU DeviceRealize common handler */ bool cpu_common_realize(CPUState *cpu, Error **errp); -void cpu_exec_unrealizefn(CPUState *cpu); +/** cpu_common_realize: CPU DeviceUnrealize common handler */ +void cpu_common_unrealize(CPUState *cpu); + void cpu_exec_reset_hold(CPUState *cpu); extern const VMStateDescription vmstate_cpu_common; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index b07e9c3ff9..e3a1075aad 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -6956,7 +6956,7 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) return; unrealize: - cpu_exec_unrealizefn(cs); + cpu_common_unrealize(cs); } static void ppc_cpu_unrealize(DeviceState *dev) From 721c189cb60b08bacff2b9adeb6e595d04a23975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Aug 2026 16:40:51 +0200 Subject: [PATCH 519/860] hw/cpu: Extract cpu_exec_realize() out of cpu_common_realizefn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpu_common_realizefn() contains code only used by system mode emulation. Extract it to a new cpu_exec_realize() helper. In the next commit this helper will be moved to cpu-system.c where it belongs. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-8-philmd@oss.qualcomm.com> --- hw/core/cpu-common.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 59cd489b71..cd7c8763cf 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -246,9 +246,8 @@ bool cpu_common_realize(CPUState *cpu, Error **errp) return true; } -static void cpu_exec_realize(DeviceState *dev, Error **errp) +static void cpu_exec_realize(CPUState *cpu, Error **errp) { - CPUState *cpu = CPU(dev); Object *machine = qdev_get_machine(); /* qdev_get_machine() can return something that's not TYPE_MACHINE @@ -264,10 +263,15 @@ static void cpu_exec_realize(DeviceState *dev, Error **errp) } } - if (dev->hotplugged) { + if (DEVICE(cpu)->hotplugged) { cpu_synchronize_post_init(cpu); cpu_resume(cpu); } +} + +static void cpu_common_realizefn(DeviceState *dev, Error **errp) +{ + cpu_exec_realize(CPU(dev), errp); /* NOTE: latest generic point where the cpu is fully realized */ } @@ -379,7 +383,7 @@ static void cpu_common_class_init(ObjectClass *klass, const void *data) k->gdb_read_register = cpu_common_gdb_read_register; k->gdb_write_register = cpu_common_gdb_write_register; set_bit(DEVICE_CATEGORY_CPU, dc->categories); - dc->realize = cpu_exec_realize; + dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; rc->phases.hold = cpu_common_reset_hold; rc->phases.exit = cpu_common_reset_exit; From 398913c47b334d174f235983e002a4bdf6d3d409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Aug 2026 16:48:01 +0200 Subject: [PATCH 520/860] hw/cpu: Move system-specific cpu_exec_realize() to cpu-system.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current cpu_exec_realize() body only contains system-mode related code. Move that method out of cpu-common.c to cpu-system.c, removing the system / machine mentions in this common file. Add an empty stub for user-mode. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20260811183410.22428-9-philmd@oss.qualcomm.com> --- hw/core/cpu-common.c | 24 ------------------------ hw/core/cpu-internal.h | 1 + hw/core/cpu-system.c | 27 +++++++++++++++++++++++++++ hw/core/cpu-user.c | 5 +++++ 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index cd7c8763cf..4f4c87f33a 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -33,7 +33,6 @@ #include "exec/log.h" #include "exec/gdbstub.h" #include "system/tcg.h" -#include "hw/core/boards.h" #include "hw/core/qdev-properties.h" #include "trace.h" #ifdef CONFIG_PLUGIN @@ -246,29 +245,6 @@ bool cpu_common_realize(CPUState *cpu, Error **errp) return true; } -static void cpu_exec_realize(CPUState *cpu, Error **errp) -{ - Object *machine = qdev_get_machine(); - - /* qdev_get_machine() can return something that's not TYPE_MACHINE - * if this is one of the user-only emulators; in that case there's - * no need to check the ignore_memory_transaction_failures board flag. - */ - if (object_dynamic_cast(machine, TYPE_MACHINE)) { - MachineClass *mc = MACHINE_GET_CLASS(machine); - - if (mc) { - cpu->ignore_memory_transaction_failures = - mc->ignore_memory_transaction_failures; - } - } - - if (DEVICE(cpu)->hotplugged) { - cpu_synchronize_post_init(cpu); - cpu_resume(cpu); - } -} - static void cpu_common_realizefn(DeviceState *dev, Error **errp) { cpu_exec_realize(CPU(dev), errp); diff --git a/hw/core/cpu-internal.h b/hw/core/cpu-internal.h index ae6a31bca2..44715b4432 100644 --- a/hw/core/cpu-internal.h +++ b/hw/core/cpu-internal.h @@ -15,6 +15,7 @@ void cpu_class_init_props(DeviceClass *dc); void cpu_exec_class_post_init(CPUClass *cc); void cpu_exec_init(CPUState *cpu); +void cpu_exec_realize(CPUState *cpu, Error **errp); void cpu_vmstate_register(CPUState *cpu); void cpu_vmstate_unregister(CPUState *cpu); diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 8a9f2fcea8..a64e8c2b68 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -25,10 +25,12 @@ #include "exec/target_page.h" #include "system/memory.h" #include "qemu/target-info.h" +#include "hw/core/boards.h" #include "hw/core/qdev.h" #include "hw/core/qdev-properties.h" #include "hw/core/sysemu-cpu-ops.h" #include "migration/vmstate.h" +#include "system/hw_accel.h" #include "system/tcg.h" #include "cpu-internal.h" @@ -221,6 +223,31 @@ static int cpu_common_pre_load(void *opaque) return 0; } +void cpu_exec_realize(CPUState *cpu, Error **errp) +{ + Object *machine = qdev_get_machine(); + + /* + * qdev_get_machine() can return something that's not TYPE_MACHINE + * if this is one of the user-only emulators; in that case there's + * no need to check the ignore_memory_transaction_failures board flag. + */ + if (object_dynamic_cast(machine, TYPE_MACHINE)) { + MachineClass *mc = MACHINE_GET_CLASS(machine); + + if (mc) { + cpu->ignore_memory_transaction_failures = + mc->ignore_memory_transaction_failures; + } + } + + if (DEVICE(cpu)->hotplugged) { + cpu_synchronize_post_init(cpu); + cpu_resume(cpu); + } + +} + static bool cpu_common_exception_index_needed(void *opaque) { CPUState *cpu = opaque; diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c index c2cb00a85d..1e38c88f9b 100644 --- a/hw/core/cpu-user.c +++ b/hw/core/cpu-user.c @@ -38,6 +38,11 @@ void cpu_exec_init(CPUState *cpu) /* nothing to do */ } +void cpu_exec_realize(CPUState *cpu, Error **errp) +{ + /* nothing to do */ +} + void cpu_vmstate_register(CPUState *cpu) { assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || From f37b245e564d7004f6a5f61a44cc021dda45f13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Feb 2024 14:38:31 +0100 Subject: [PATCH 521/860] hw/nmi: Use object_child_foreach_recursive() in nmi_children() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace object_child_foreach() and recursion by a single object_child_foreach_recursive() call. Propagate the returned value so callers can check it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Marc-André Lureau Message-Id: <20260812121232.71958-2-philmd@oss.qualcomm.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/nmi.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/core/nmi.c b/hw/core/nmi.c index 4b447e126b..2272404657 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -31,8 +31,6 @@ struct do_nmi_s { bool handled; }; -static void nmi_children(Object *o, struct do_nmi_s *ns); - static int do_nmi(Object *o, void *opaque) { struct do_nmi_s *ns = opaque; @@ -47,14 +45,13 @@ static int do_nmi(Object *o, void *opaque) return -1; } } - nmi_children(o, ns); return 0; } -static void nmi_children(Object *o, struct do_nmi_s *ns) +static int nmi_children(Object *o, struct do_nmi_s *ns) { - object_child_foreach(o, do_nmi, ns); + return object_child_foreach_recursive(o, do_nmi, ns); } void nmi_monitor_handle(int cpu_index, Error **errp) @@ -65,10 +62,9 @@ void nmi_monitor_handle(int cpu_index, Error **errp) .handled = false }; - nmi_children(object_get_root(), &ns); - if (ns.handled) { + if (nmi_children(object_get_root(), &ns)) { error_propagate(errp, ns.err); - } else { + } else if (!ns.handled) { error_setg(errp, "machine does not provide NMIs"); } } From c489b04fbc94e7f738704b2b85e88d92ecf5cc6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Feb 2024 14:28:25 +0100 Subject: [PATCH 522/860] hw/s390x/virtio-ccw: Always inject NMI to first CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can trigger NMI from HMP or QMP. QEMU maps the NMI to the s390x per-CPU 'RESTART' interrupt. Linux guests usually setup this interrupt to trigger kdump or crash. Such crashdump can be triggered in QEMU by HMP "nmi" or QMP "inject-nmi" commands. Using QMP, since we can not select a particular CPU, the first CPU is used (CPU#0). See the documentation from commit 795dc6e4 ("watchdog: Add new Virtual Watchdog action INJECT-NMI"): @inject-nmi: a non-maskable interrupt is injected into the first VCPU (all VCPUS on x86) (since 2.4) While we can select a particular CPU on HMP, the guest behavior is expected to be the same if using CPU #N or CPU #0. Since always using CPU#0 simplifies API maintenance , update s390_nmi() to inject NMI to the first CPU. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: David Hildenbrand Reviewed-by: Eric Farman Reviewed-by: Marc-André Lureau Message-Id: <20260812121232.71958-3-philmd@oss.qualcomm.com> Signed-off-by: Philippe Mathieu-Daudé --- hmp-commands.hx | 5 +++-- hw/s390x/s390-virtio-ccw.c | 4 +--- qapi/machine.json | 7 ++++--- qapi/run-state.json | 6 ++++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 7f43cf537f..a29f02b623 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -865,12 +865,13 @@ ERST .name = "nmi", .args_type = "", .params = "", - .help = "inject an NMI", + .help = "Inject an NMI, in a machine-specific way", .cmd = hmp_nmi, }, SRST ``nmi`` *cpu* - Inject an NMI on the default CPU (x86/s390) or all CPUs (ppc64). + Inject an NMI, in a machine-specific way. + Not all machines implement NMI handling. ERST { diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 55131c1a44..a2f373b119 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -727,9 +727,7 @@ static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, static void s390_nmi(NMIState *n, int cpu_index, Error **errp) { - CPUState *cs = qemu_get_cpu(cpu_index); - - s390_cpu_restart(S390_CPU(cs)); + s390_cpu_restart(S390_CPU(first_cpu)); } static inline bool machine_get_aes_key_wrap(Object *obj, Error **errp) diff --git a/qapi/machine.json b/qapi/machine.json index 9b2248038f..2d63c1bac3 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -432,9 +432,10 @@ ## # @inject-nmi: # -# Injects a Non-Maskable Interrupt into the default CPU (x86/s390) or -# all CPUs (ppc64). The command fails when the guest doesn't support -# injecting. +# Injects a Non-Maskable Interrupt (machine specific: for example on +# s390x CCW only the first vCPU receives the NMI, but on x86 machines +# all vCPUs receive it). The command fails when the guest doesn't +# support injecting. # # Since: 0.14 # diff --git a/qapi/run-state.json b/qapi/run-state.json index a5771ad468..e4fdead1e0 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -317,8 +317,10 @@ # # @none: nothing is done # -# @inject-nmi: a non-maskable interrupt is injected into the first -# VCPU (all VCPUS on x86) (since 2.4) +# @inject-nmi: a non-maskable interrupt is injected (machine +# specific: for example on s390x CCW only the first vCPU +# receives the NMI, but on x86 machines all vCPUs receive +# it). (since 2.4) # # Since: 2.1 ## From 9dad341a527a8fea796ba07803f41819addbceec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Feb 2024 14:30:43 +0100 Subject: [PATCH 523/860] hw/nmi: Remove @cpu_index argument from NMIClass::nmi_monitor_handler() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only s390x was using the 'cpu_index' argument, but since the previous commit it isn't anymore (it use the first cpu). Since this argument is now completely unused, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Marc-André Lureau Message-Id: <20260812121232.71958-4-philmd@oss.qualcomm.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/nmi.c | 2 +- hw/hppa/machine.c | 2 +- hw/i386/x86.c | 2 +- hw/intc/m68k_irqc.c | 2 +- hw/m68k/q800-glue.c | 2 +- hw/misc/macio/gpio.c | 2 +- hw/ppc/pnv.c | 2 +- hw/ppc/spapr.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- include/hw/core/nmi.h | 8 +++++++- 10 files changed, 16 insertions(+), 10 deletions(-) diff --git a/hw/core/nmi.c b/hw/core/nmi.c index 2272404657..ff6454437c 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -40,7 +40,7 @@ static int do_nmi(Object *o, void *opaque) NMIClass *nc = NMI_GET_CLASS(n); ns->handled = true; - nc->nmi_monitor_handler(n, ns->cpu_index, &ns->err); + nc->nmi_monitor_handler(n, &ns->err); if (ns->err) { return -1; } diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index d762163ddf..2b44debc38 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -829,7 +829,7 @@ static void hppa_machine_reset(MachineState *ms, ResetType type) cpu[0]->env.cmdline_or_bootorder = 'c'; } -static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) +static void hppa_nmi(NMIState *ns, Error **errp) { CPUState *cs; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index dc7f0d56b0..0ea5453403 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -147,7 +147,7 @@ static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } -static void x86_nmi(NMIState *n, int cpu_index, Error **errp) +static void x86_nmi(NMIState *ns, Error **errp) { /* cpu index isn't used */ CPUState *cs; diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 68ddb5351b..bc41b44d45 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -70,7 +70,7 @@ static void m68k_irqc_instance_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), m68k_set_irq, M68K_IRQC_LEVEL_NUM); } -static void m68k_nmi(NMIState *n, int cpu_index, Error **errp) +static void m68k_nmi(NMIState *n, Error **errp) { m68k_set_irq(n, M68K_IRQC_LEVEL_7, 1); } diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index ac9062c648..b13e5ef95f 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -159,7 +159,7 @@ static void glue_auxmode_set_irq(void *opaque, int irq, int level) s->auxmode = level; } -static void glue_nmi(NMIState *n, int cpu_index, Error **errp) +static void glue_nmi(NMIState *n, Error **errp) { GLUEState *s = GLUE(n); diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 1a7c534d65..1bdca728f9 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -188,7 +188,7 @@ static void macio_gpio_reset(DeviceState *dev) macio_set_gpio(s, 1, true); } -static void macio_gpio_nmi(NMIState *n, int cpu_index, Error **errp) +static void macio_gpio_nmi(NMIState *n, Error **errp) { macio_set_gpio(MACIO_GPIO(n), 9, true); macio_set_gpio(MACIO_GPIO(n), 9, false); diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index c0cb45dbfb..8676be7bbb 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -3552,7 +3552,7 @@ static void pnv_cpu_do_nmi(PnvChip *chip, PowerPCCPU *cpu, void *opaque) async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(0)); } -static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) +static void pnv_nmi(NMIState *ns, Error **errp) { PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); int i; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 7e284edd7d..c376a58872 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3550,7 +3550,7 @@ void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) } } -static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) +static void spapr_nmi(NMIState *ns, Error **errp) { CPUState *cs; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a2f373b119..ac04245d65 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -725,7 +725,7 @@ static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, return NULL; } -static void s390_nmi(NMIState *n, int cpu_index, Error **errp) +static void s390_nmi(NMIState *ns, Error **errp) { s390_cpu_restart(S390_CPU(first_cpu)); } diff --git a/include/hw/core/nmi.h b/include/hw/core/nmi.h index fff41bebc6..4c4ce79071 100644 --- a/include/hw/core/nmi.h +++ b/include/hw/core/nmi.h @@ -37,7 +37,13 @@ typedef struct NMIState NMIState; struct NMIClass { InterfaceClass parent_class; - void (*nmi_monitor_handler)(NMIState *n, int cpu_index, Error **errp); + /** + * nmi_monitor_handler: Callback to handle NMI notifications. + * + * @ns: Class #NMIState state + * @errp: pointer to error object + */ + void (*nmi_monitor_handler)(NMIState *ns, Error **errp); }; void nmi_monitor_handle(int cpu_index, Error **errp); From 64011ef0d93fa892333782643ec544cc10504651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Feb 2024 14:31:25 +0100 Subject: [PATCH 524/860] hw/nmi: Remove @cpu_index argument from nmi_inject() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nmi_monitor_handle() is not related to the monitor, rename it as nmi_inject(). Return a boolean value indicating success / failure as recommended by the Error API since commit e3fe3988d7 ("error: Document Error API usage rules"). The 'cpu_index' argument is not used, remove it. This officially drops the current CPU for HMP command. Document nmi_inject() as suggested by Peter Maydell in https://lore.kernel.org/qemu-devel/CAFEAcA-yALySmCJLbitCmYpiZKUXJNOavGJG9RYeo8fKqz7gcw@mail.gmail.com/. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Marc-André Lureau Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20260812121232.71958-5-philmd@oss.qualcomm.com> --- hmp-commands.hx | 2 +- hw/core/nmi.c | 9 ++++----- hw/ipmi/ipmi.c | 3 +-- hw/watchdog/watchdog.c | 2 +- include/hw/core/nmi.h | 24 +++++++++++++++++++++++- system/cpus.c | 3 +-- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index a29f02b623..43ff220b5f 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -869,7 +869,7 @@ ERST .cmd = hmp_nmi, }, SRST -``nmi`` *cpu* +``nmi`` Inject an NMI, in a machine-specific way. Not all machines implement NMI handling. ERST diff --git a/hw/core/nmi.c b/hw/core/nmi.c index ff6454437c..2d890f2995 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -22,11 +22,8 @@ #include "qemu/osdep.h" #include "hw/core/nmi.h" #include "qapi/error.h" -#include "qemu/module.h" -#include "monitor/monitor.h" struct do_nmi_s { - int cpu_index; Error *err; bool handled; }; @@ -54,19 +51,21 @@ static int nmi_children(Object *o, struct do_nmi_s *ns) return object_child_foreach_recursive(o, do_nmi, ns); } -void nmi_monitor_handle(int cpu_index, Error **errp) +bool nmi_inject(Error **errp) { struct do_nmi_s ns = { - .cpu_index = cpu_index, .err = NULL, .handled = false }; if (nmi_children(object_get_root(), &ns)) { error_propagate(errp, ns.err); + return false; } else if (!ns.handled) { error_setg(errp, "machine does not provide NMIs"); + return false; } + return true; } static const TypeInfo nmi_info = { diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 74818ff3ce..dedf23cb99 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -59,8 +59,7 @@ static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) if (checkonly) { return 0; } - /* We don't care what CPU we use. */ - nmi_monitor_handle(0, NULL); + nmi_inject(NULL); return 0; case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP: diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 0842fe373a..5f764a0c1b 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -81,7 +81,7 @@ void watchdog_perform_action(void) case WATCHDOG_ACTION_INJECT_NMI: qapi_event_send_watchdog(WATCHDOG_ACTION_INJECT_NMI); - nmi_monitor_handle(0, NULL); + nmi_inject(NULL); break; default: diff --git a/include/hw/core/nmi.h b/include/hw/core/nmi.h index 4c4ce79071..851be28257 100644 --- a/include/hw/core/nmi.h +++ b/include/hw/core/nmi.h @@ -46,6 +46,28 @@ struct NMIClass { void (*nmi_monitor_handler)(NMIState *ns, Error **errp); }; -void nmi_monitor_handle(int cpu_index, Error **errp); +/** + * nmi_inject: Inject an NMI, in a machine-specific way + * @errp: pointer to error object + * + * This function injects an NMI, in a machine-specific way. The + * intention is that this should typically trigger a guest kernel + * dump or reboot, and might happen as a result of user request + * from the monitor, watchdog timeouts, and similar events. + * (For example on the x86 PC it triggers an NMI on all CPUs, + * and on s390 it triggers the RESTART interrupt on the first CPU.) + * + * The NMI is injected by looking for a QOM object which implements + * the TYPE_NMI interface, and calling its nmi_monitor_handler method. Usually + * it is the machine model class that implements this interface. + * + * Not all machines implement NMI handling; this function + * will return an error if used on a machine which does not + * implement NMIs. + * + * On success, return %true. + * On failure, store an error through @errp and return %false. + */ +bool nmi_inject(Error **errp); #endif /* NMI_H */ diff --git a/system/cpus.c b/system/cpus.c index 97e5a5edee..9758cda463 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" -#include "monitor/monitor.h" #include "qemu/coroutine-tls.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" @@ -926,6 +925,6 @@ exit: void qmp_inject_nmi(Error **errp) { - nmi_monitor_handle(monitor_get_cpu_index(monitor_cur()), errp); + nmi_inject(errp); } From caabebf76b0203e37108c3c6902cc146bb7dd0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Aug 2026 12:20:36 +0200 Subject: [PATCH 525/860] hw/nmi: Rename nmi_monitor_handler() -> raise_nmi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nmi_monitor_handler() is not related to the monitor, rename it as raise_nmi(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812121232.71958-6-philmd@oss.qualcomm.com> --- hw/core/nmi.c | 2 +- hw/hppa/machine.c | 2 +- hw/i386/x86.c | 2 +- hw/intc/m68k_irqc.c | 2 +- hw/m68k/q800-glue.c | 2 +- hw/misc/macio/gpio.c | 2 +- hw/ppc/pnv.c | 2 +- hw/ppc/spapr.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- include/hw/core/nmi.h | 10 ++++++---- 10 files changed, 15 insertions(+), 13 deletions(-) diff --git a/hw/core/nmi.c b/hw/core/nmi.c index 2d890f2995..a6edf4fbf0 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -37,7 +37,7 @@ static int do_nmi(Object *o, void *opaque) NMIClass *nc = NMI_GET_CLASS(n); ns->handled = true; - nc->nmi_monitor_handler(n, &ns->err); + nc->raise_nmi(n, &ns->err); if (ns->err) { return -1; } diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 2b44debc38..717cfde61d 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -851,7 +851,7 @@ static void hppa_machine_common_class_init(ObjectClass *oc, const void *data) mc->default_ram_id = "hppa.ram"; mc->default_nic = "tulip"; - nc->nmi_monitor_handler = hppa_nmi; + nc->raise_nmi = hppa_nmi; } static void HP_B160L_machine_init_class_init(ObjectClass *oc, const void *data) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 0ea5453403..d1414ff63d 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -389,7 +389,7 @@ static void x86_machine_class_init(ObjectClass *oc, const void *data) mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->kvm_type = x86_kvm_type; - nc->nmi_monitor_handler = x86_nmi; + nc->raise_nmi = x86_nmi; object_class_property_add(oc, X86_MACHINE_SMM, "OnOffAuto", x86_machine_get_smm, x86_machine_set_smm, diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index bc41b44d45..29f758dafd 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -97,7 +97,7 @@ static void m68k_irqc_class_init(ObjectClass *oc, const void *data) InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); device_class_set_props(dc, m68k_irqc_properties); - nc->nmi_monitor_handler = m68k_nmi; + nc->raise_nmi = m68k_nmi; device_class_set_legacy_reset(dc, m68k_irqc_reset); dc->vmsd = &vmstate_m68k_irqc; ic->get_statistics = m68k_irqc_get_statistics; diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index b13e5ef95f..265af90a8e 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -237,7 +237,7 @@ static void glue_class_init(ObjectClass *klass, const void *data) dc->vmsd = &vmstate_glue; device_class_set_props(dc, glue_properties); rc->phases.hold = glue_reset_hold; - nc->nmi_monitor_handler = glue_nmi; + nc->raise_nmi = glue_nmi; } static const TypeInfo glue_info_types[] = { diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index 1bdca728f9..db0c4ecb3c 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -201,7 +201,7 @@ static void macio_gpio_class_init(ObjectClass *oc, const void *data) device_class_set_legacy_reset(dc, macio_gpio_reset); dc->vmsd = &vmstate_macio_gpio; - nc->nmi_monitor_handler = macio_gpio_nmi; + nc->raise_nmi = macio_gpio_nmi; } static const TypeInfo macio_gpio_init_info = { diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 8676be7bbb..a9f7424586 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -3583,7 +3583,7 @@ static void pnv_machine_class_init(ObjectClass *oc, const void *data) mc->default_ram_size = 1 * GiB; mc->default_ram_id = "pnv.ram"; ispc->print_info = pnv_pic_print_info; - nc->nmi_monitor_handler = pnv_nmi; + nc->raise_nmi = pnv_nmi; object_class_property_add_bool(oc, "hb-mode", pnv_machine_get_hb, pnv_machine_set_hb); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index c376a58872..1bc90ceed3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4652,7 +4652,7 @@ static void spapr_machine_class_init(ObjectClass *oc, const void *data) mc->nvdimm_supported = true; smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED; fwc->get_dev_path = spapr_get_fw_dev_path; - nc->nmi_monitor_handler = spapr_nmi; + nc->raise_nmi = spapr_nmi; vhc->cpu_in_nested = spapr_cpu_in_nested; vhc->deliver_hv_excp = spapr_exit_nested; vhc->hypercall = emulate_spapr_hypercall; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index ac04245d65..21d9f51ddc 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -830,7 +830,7 @@ static void ccw_machine_class_init(ObjectClass *oc, const void *data) hc->plug = s390_machine_device_plug; hc->unplug_request = s390_machine_device_unplug_request; hc->unplug = s390_machine_device_unplug; - nc->nmi_monitor_handler = s390_nmi; + nc->raise_nmi = s390_nmi; mc->default_ram_id = "s390.ram"; mc->default_nic = "virtio-net-ccw"; dsi->qmp_dump_skeys = s390_qmp_dump_skeys; diff --git a/include/hw/core/nmi.h b/include/hw/core/nmi.h index 851be28257..a8feac3b67 100644 --- a/include/hw/core/nmi.h +++ b/include/hw/core/nmi.h @@ -38,12 +38,14 @@ struct NMIClass { InterfaceClass parent_class; /** - * nmi_monitor_handler: Callback to handle NMI notifications. - * + * raise_nmi: Callback to handle NMI notifications. * @ns: Class #NMIState state * @errp: pointer to error object + * + * Called by nmi_inject() to perform the machine-specific + * action when a NMI is requested. */ - void (*nmi_monitor_handler)(NMIState *ns, Error **errp); + void (*raise_nmi)(NMIState *ns, Error **errp); }; /** @@ -58,7 +60,7 @@ struct NMIClass { * and on s390 it triggers the RESTART interrupt on the first CPU.) * * The NMI is injected by looking for a QOM object which implements - * the TYPE_NMI interface, and calling its nmi_monitor_handler method. Usually + * the TYPE_NMI interface, and calling its raise_nmi method. Usually * it is the machine model class that implements this interface. * * Not all machines implement NMI handling; this function From acfbe07619b533238a376ee1dc4cdced56cb3a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Aug 2026 12:24:19 +0200 Subject: [PATCH 526/860] hw/nmi: Remove unused @errp argument from raise_nmi() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not a single handler update @errp. The single user is nmi_inject() filling with "machine does not provide NMIs". Remove the unused argument from the raise_nmi() callback, simplifying the methods in hw/core/nmi.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812121232.71958-7-philmd@oss.qualcomm.com> --- hw/core/nmi.c | 32 ++++++-------------------------- hw/hppa/machine.c | 2 +- hw/i386/x86.c | 2 +- hw/intc/m68k_irqc.c | 2 +- hw/m68k/q800-glue.c | 2 +- hw/misc/macio/gpio.c | 2 +- hw/ppc/pnv.c | 2 +- hw/ppc/spapr.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- include/hw/core/nmi.h | 3 +-- 10 files changed, 15 insertions(+), 36 deletions(-) diff --git a/hw/core/nmi.c b/hw/core/nmi.c index a6edf4fbf0..c44b0cf892 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -23,45 +23,25 @@ #include "hw/core/nmi.h" #include "qapi/error.h" -struct do_nmi_s { - Error *err; - bool handled; -}; - static int do_nmi(Object *o, void *opaque) { - struct do_nmi_s *ns = opaque; + bool *handled = opaque; NMIState *n = (NMIState *) object_dynamic_cast(o, TYPE_NMI); if (n) { - NMIClass *nc = NMI_GET_CLASS(n); - - ns->handled = true; - nc->raise_nmi(n, &ns->err); - if (ns->err) { - return -1; - } + *handled = true; + NMI_GET_CLASS(n)->raise_nmi(n); } return 0; } -static int nmi_children(Object *o, struct do_nmi_s *ns) -{ - return object_child_foreach_recursive(o, do_nmi, ns); -} - bool nmi_inject(Error **errp) { - struct do_nmi_s ns = { - .err = NULL, - .handled = false - }; + bool handled = false; - if (nmi_children(object_get_root(), &ns)) { - error_propagate(errp, ns.err); - return false; - } else if (!ns.handled) { + object_child_foreach_recursive(object_get_root(), do_nmi, &handled); + if (!handled) { error_setg(errp, "machine does not provide NMIs"); return false; } diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 717cfde61d..98931481b2 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -829,7 +829,7 @@ static void hppa_machine_reset(MachineState *ms, ResetType type) cpu[0]->env.cmdline_or_bootorder = 'c'; } -static void hppa_nmi(NMIState *ns, Error **errp) +static void hppa_nmi(NMIState *ns) { CPUState *cs; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index d1414ff63d..f8ba3244e2 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -147,7 +147,7 @@ static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } -static void x86_nmi(NMIState *ns, Error **errp) +static void x86_nmi(NMIState *ns) { /* cpu index isn't used */ CPUState *cs; diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 29f758dafd..47f626c0fa 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -70,7 +70,7 @@ static void m68k_irqc_instance_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), m68k_set_irq, M68K_IRQC_LEVEL_NUM); } -static void m68k_nmi(NMIState *n, Error **errp) +static void m68k_nmi(NMIState *n) { m68k_set_irq(n, M68K_IRQC_LEVEL_7, 1); } diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index 265af90a8e..223e7d9019 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -159,7 +159,7 @@ static void glue_auxmode_set_irq(void *opaque, int irq, int level) s->auxmode = level; } -static void glue_nmi(NMIState *n, Error **errp) +static void glue_nmi(NMIState *n) { GLUEState *s = GLUE(n); diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index db0c4ecb3c..ba001f7b20 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -188,7 +188,7 @@ static void macio_gpio_reset(DeviceState *dev) macio_set_gpio(s, 1, true); } -static void macio_gpio_nmi(NMIState *n, Error **errp) +static void macio_gpio_nmi(NMIState *n) { macio_set_gpio(MACIO_GPIO(n), 9, true); macio_set_gpio(MACIO_GPIO(n), 9, false); diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a9f7424586..f0413639f9 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -3552,7 +3552,7 @@ static void pnv_cpu_do_nmi(PnvChip *chip, PowerPCCPU *cpu, void *opaque) async_run_on_cpu(CPU(cpu), pnv_cpu_do_nmi_on_cpu, RUN_ON_CPU_HOST_INT(0)); } -static void pnv_nmi(NMIState *ns, Error **errp) +static void pnv_nmi(NMIState *ns) { PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); int i; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 1bc90ceed3..20e024907b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -3550,7 +3550,7 @@ void spapr_do_system_reset_on_cpu(CPUState *cs, run_on_cpu_data arg) } } -static void spapr_nmi(NMIState *ns, Error **errp) +static void spapr_nmi(NMIState *ns) { CPUState *cs; diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 21d9f51ddc..06e5def909 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -725,7 +725,7 @@ static HotplugHandler *s390_get_hotplug_handler(MachineState *machine, return NULL; } -static void s390_nmi(NMIState *ns, Error **errp) +static void s390_nmi(NMIState *ns) { s390_cpu_restart(S390_CPU(first_cpu)); } diff --git a/include/hw/core/nmi.h b/include/hw/core/nmi.h index a8feac3b67..d71d92dfbb 100644 --- a/include/hw/core/nmi.h +++ b/include/hw/core/nmi.h @@ -40,12 +40,11 @@ struct NMIClass { /** * raise_nmi: Callback to handle NMI notifications. * @ns: Class #NMIState state - * @errp: pointer to error object * * Called by nmi_inject() to perform the machine-specific * action when a NMI is requested. */ - void (*raise_nmi)(NMIState *ns, Error **errp); + void (*raise_nmi)(NMIState *ns); }; /** From c6f9b1dc9564760959e915665d1b4f5d61105be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 11 Aug 2026 12:43:06 +0200 Subject: [PATCH 527/860] hw/nmi: Raise NMI line only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only expect one device in the system to implement the TYPE_NMI interface (typically the machine, but in a few cases for e.g. m68k and ppc this is an interrupt controller or similar device); so we don't need to keep walking the whole QOM tree once we've found it. As no machine type creates more than one object implementing TYPE_NMI, this is not a behaviour change. Suggested-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Marc-André Lureau Message-Id: <20260812121232.71958-8-philmd@oss.qualcomm.com> --- hw/core/nmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/core/nmi.c b/hw/core/nmi.c index c44b0cf892..84f2166598 100644 --- a/hw/core/nmi.c +++ b/hw/core/nmi.c @@ -31,6 +31,11 @@ static int do_nmi(Object *o, void *opaque) if (n) { *handled = true; NMI_GET_CLASS(n)->raise_nmi(n); + /* + * We expect only one object to implement TYPE_NMI, so once + * we've asked it to deliver the NMI we can stop looking. + */ + return 1; } return 0; From 561710e5d2356ed10456e36224a8f4ce9592afa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 10:58:40 +0200 Subject: [PATCH 528/860] hexagon: Remove unnecessary 'monitor/monitor.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Monitor type is used in these 2 files, as a pointer. Since the type is forward-declared in "qemu/typedefs.h", which all source files include via "qemu/osdep.h", we do not need to include it. Do however include "exec/hwaddr.h" and "exec/mmu-access-type.h" which declare the types used by hex_tlb_find_match prototype: extern bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA, MMUAccessType access_type, hwaddr *PA, int *prot, ^^^^^^^^^^^^^ ^^^^^^ uint64_t *size, int32_t *excp, int mmu_idx); Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-2-philmd@oss.qualcomm.com> --- include/hw/hexagon/hexagon_tlb.h | 2 +- target/hexagon/hex_mmu.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/hw/hexagon/hexagon_tlb.h b/include/hw/hexagon/hexagon_tlb.h index 90d9ed8404..760dc1ea81 100644 --- a/include/hw/hexagon/hexagon_tlb.h +++ b/include/hw/hexagon/hexagon_tlb.h @@ -12,7 +12,7 @@ #include "qom/object.h" #include "exec/hwaddr.h" #include "exec/mmu-access-type.h" -#include "monitor/monitor.h" + #define TYPE_HEXAGON_TLB "hexagon-tlb" OBJECT_DECLARE_SIMPLE_TYPE(HexagonTLBState, HEXAGON_TLB) diff --git a/target/hexagon/hex_mmu.h b/target/hexagon/hex_mmu.h index 4f556c715a..6aa450b941 100644 --- a/target/hexagon/hex_mmu.h +++ b/target/hexagon/hex_mmu.h @@ -7,8 +7,9 @@ #ifndef HEXAGON_MMU_H #define HEXAGON_MMU_H +#include "exec/hwaddr.h" +#include "exec/mmu-access-type.h" #include "cpu.h" -#include "monitor/monitor.h" extern void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value); extern uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA); From c5162b8423cc96afa46d2a9ac9cc3adba9436e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 18:03:10 +0200 Subject: [PATCH 529/860] net/vhost-vdpa: Include missing 'qemu/iov.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vhost-vdpa.c uses methods declared in the "qemu/iov." header. Include it otherwise we get when refactoring unrelated headers: ../net/vhost-vdpa.c: In function ‘vhost_vdpa_net_load_cmd’: ../net/vhost-vdpa.c:714:24: error: implicit declaration of function ‘iov_size’ 714 | size_t data_size = iov_size(data_sg, data_num), cmd_size; | ^~~~~~~~ ../net/vhost-vdpa.c:714:24: error: nested extern declaration of ‘iov_size’ ../net/vhost-vdpa.c:742:5: error: implicit declaration of function ‘iov_from_buf’ 742 | iov_from_buf(out_cursor, 1, 0, &ctrl, sizeof(ctrl)); | ^~~~~~~~~~~~ ../net/vhost-vdpa.c:742:5: error: nested extern declaration of ‘iov_from_buf’ ../net/vhost-vdpa.c:744:5: error: implicit declaration of function ‘iov_to_buf’ 744 | iov_to_buf(data_sg, data_num, 0, | ^~~~~~~~~~ ../net/vhost-vdpa.c:744:5: error: nested extern declaration of ‘iov_to_buf’ ../net/vhost-vdpa.c:748:5: error: implicit declaration of function ‘iov_copy’ 748 | iov_copy(&out, 1, out_cursor, 1, 0, cmd_size); | ^~~~~~~~ Fixes: bd907ae4b00 ("vdpa: manual forward CVQ buffers") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Michael S. Tsirkin Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-3-philmd@oss.qualcomm.com> --- net/vhost-vdpa.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index c526c2b2dc..1052361a4a 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -17,6 +17,7 @@ #include "hw/virtio/vhost-vdpa.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include "qemu/log.h" #include "qemu/memalign.h" #include "qemu/option.h" From 69cf1ebaf569ee2c7df21153ac8b2f14e65710d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 11:19:55 +0200 Subject: [PATCH 530/860] tests/unit: Include 'qemu/main-loop.h' header in test-util-sockets.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test-util-sockets.c calls qemu_init_main_loop(), itself declared in the "qemu/main-loop.h" header. Include the latter to avoid when refactoring unrelated headers: ../tests/unit/test-util-sockets.c:553:5: error: call to undeclared function 'qemu_init_main_loop' 553 | qemu_init_main_loop(&error_abort); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-4-philmd@oss.qualcomm.com> --- tests/unit/test-util-sockets.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index ee66d727c3..ab3f39c3ef 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "qemu/sockets.h" #include "qapi/error.h" #include "socket-helpers.h" From 977d5b5018f13f2a488c0420755f612fc8faf1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 10:57:02 +0200 Subject: [PATCH 531/860] qapi/qmp-dispatch: Include 'qemu/aio-wait.h' and 'monitor/monitor.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qmp-dispatch.c calls aio_wait_kick() and monitor_cur(). Include the header declaring them in order to avoid the following build failure when refactoring unrelated headers: ../qapi/qmp-dispatch.c:126:12: error: call to undeclared function 'monitor_cur' 126 | assert(monitor_cur() == NULL); | ^ ../qapi/qmp-dispatch.c:141:5: error: call to undeclared function 'aio_wait_kick' 141 | aio_wait_kick(); | ^ Fixes: 41725fa7eda ("qmp: Call monitor_set_cur() only in qmp_dispatch()") Fixes: fc1a2ec7da5 ("monitor: Fix deadlock in monitor_cleanup") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-5-philmd@oss.qualcomm.com> --- qapi/qmp-dispatch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index e3897d5197..965cad6499 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qemu/aio.h" +#include "qemu/aio-wait.h" #include "qapi/compat-policy.h" #include "qapi/error.h" #include "qapi/qmp-registry.h" @@ -24,6 +25,7 @@ #include "qobject/qbool.h" #include "qemu/coroutine.h" #include "qemu/main-loop.h" +#include "monitor/monitor.h" Visitor *qobject_input_visitor_new_qmp(QObject *obj) { From 9829b98dd1ccf3a5e8e4145c50e2853d13802b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 10:58:04 +0200 Subject: [PATCH 532/860] qapi/qmp-registry: Remove unnecessary 'monitor/monitor.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-6-philmd@oss.qualcomm.com> --- include/qapi/qmp-registry.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/qapi/qmp-registry.h b/include/qapi/qmp-registry.h index e0ee1ad3ac..6146306a3a 100644 --- a/include/qapi/qmp-registry.h +++ b/include/qapi/qmp-registry.h @@ -14,7 +14,6 @@ #ifndef QAPI_QMP_DISPATCH_H #define QAPI_QMP_DISPATCH_H -#include "monitor/monitor.h" #include "qemu/queue.h" typedef void (QmpCommandFunc)(QDict *, QObject **, Error **); From cca454be70f02ee1f77474d444e38620bf60c446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 11:09:41 +0200 Subject: [PATCH 533/860] migration/hmp-cmds: Include 'block/block-global-state.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit migration-hmp-cmds.c uses types / methods declared in "block/block-global-state.h". Include the latter otherwise we get when refactoring unrelated headers: ../migration/migration-hmp-cmds.c:911:5: error: use of undeclared identifier 'BdrvNextIterator' 911 | BdrvNextIterator it; | ^ ../migration/migration-hmp-cmds.c:918:15: error: call to undeclared function 'bdrv_first' 918 | for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Reviewed-by: Dr. David Alan Gilbert Message-Id: <20260812211708.92824-7-philmd@oss.qualcomm.com> --- migration/migration-hmp-cmds.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index b5eb27467c..ad68fa23aa 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "block/qapi.h" +#include "block/block-global-state.h" #include "migration/snapshot.h" #include "monitor/hmp.h" #include "monitor/hmp-completion.h" From 79a71f3a49f106282447598b9ebb0e78cfc47425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 11:13:48 +0200 Subject: [PATCH 534/860] monitor: Include missing 'qemu/aio-wait.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both monitor.c and qmp.c use types / methods declared in "qemu/aio-wait.h". Include the latter to avoid the following errors when refactoring unrelated headers: ../monitor/monitor.c:648:5: error: call to undeclared function 'AIO_WAIT_WHILE_UNLOCKED' 648 | AIO_WAIT_WHILE_UNLOCKED(NULL, | ^ ../monitor/qmp.c:792:9: error: call to undeclared function 'aio_wait_bh_oneshot' 792 | aio_wait_bh_oneshot(iothread_get_aio_context(mon_iothread), | ^ Fixes: 9ce44e2ce26 ("qmp: Move dispatcher to a coroutine") Fixes: a5df506e129 ("monitor: implement support for deleting QMP objects") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-8-philmd@oss.qualcomm.com> --- monitor/monitor.c | 1 + monitor/qmp.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/monitor/monitor.c b/monitor/monitor.c index ed195fd97b..c75ae81534 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -30,6 +30,7 @@ #include "qapi/qapi-visit-control.h" #include "qobject/qdict.h" #include "qom/object_interfaces.h" +#include "qemu/aio-wait.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "system/qtest.h" diff --git a/monitor/qmp.c b/monitor/qmp.c index 338d37cb7e..6ae1098d39 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -23,7 +23,7 @@ */ #include "qemu/osdep.h" - +#include "qemu/aio-wait.h" #include "chardev/char-io.h" #include "monitor-internal.h" #include "qapi/error.h" From e883c338dd1997a4e06a5f3be8ea53310b3a44ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 11:10:29 +0200 Subject: [PATCH 535/860] monitor: Include missing 'qemu/lockable.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files in monitor/ use the QEMU_LOCK_GUARD() macros, which are declared in "qemu/lockable.h". Include the latter to avoid when refactoring unrelated headers: ../monitor/fds.c:146:5: error: call to undeclared function 'QEMU_LOCK_GUARD' 146 | QEMU_LOCK_GUARD(&mon->mon_lock); | ^ ../monitor/monitor.c:176:5: error: call to undeclared function 'QEMU_LOCK_GUARD' 176 | QEMU_LOCK_GUARD(&mon->mon_lock); | ^ ../monitor/qmp.c:164:5: error: call to undeclared function 'WITH_QEMU_LOCK_GUARD' 164 | WITH_QEMU_LOCK_GUARD(&mon->mon_lock) { | ^ Fixes: 0210c3b39be ("monitor: Use LOCK_GUARD macros") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-9-philmd@oss.qualcomm.com> --- monitor/fds.c | 1 + monitor/monitor.c | 1 + monitor/qmp.c | 1 + 3 files changed, 3 insertions(+) diff --git a/monitor/fds.c b/monitor/fds.c index cc35d2ec33..abe5f13487 100644 --- a/monitor/fds.c +++ b/monitor/fds.c @@ -29,6 +29,7 @@ #include "qapi/qmp/qerror.h" #include "qemu/ctype.h" #include "qemu/cutils.h" +#include "qemu/lockable.h" #include "system/runstate.h" /* file descriptors passed via SCM_RIGHTS */ diff --git a/monitor/monitor.c b/monitor/monitor.c index c75ae81534..6af09f2f6e 100644 --- a/monitor/monitor.c +++ b/monitor/monitor.c @@ -32,6 +32,7 @@ #include "qom/object_interfaces.h" #include "qemu/aio-wait.h" #include "qemu/error-report.h" +#include "qemu/lockable.h" #include "qemu/option.h" #include "system/qtest.h" #include "trace.h" diff --git a/monitor/qmp.c b/monitor/qmp.c index 6ae1098d39..aec0315775 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/aio-wait.h" +#include "qemu/lockable.h" #include "chardev/char-io.h" #include "monitor-internal.h" #include "qapi/error.h" From 25780a68e335d17ee068dcfcf49c5575ceae0591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 11:16:05 +0200 Subject: [PATCH 536/860] monitor: Include missing 'qemu/coroutine-core.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "monitor/monitor.h" declares monitor_set_cur() which use the 'Coroutine' type, itself declared in "qemu/coroutine-core.h". Include the latter to avoid when refactoring unrelated headers: In file included from ../../target/sh4/monitor.c:26: qemu/include/monitor/monitor.h:32:26: error: unknown type name 'Coroutine' 32 | Monitor *monitor_set_cur(Coroutine *co, Monitor *mon); | ^ Fixes: e69ee454b5f ("monitor: Make current monitor a per-coroutine property") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-10-philmd@oss.qualcomm.com> --- include/monitor/monitor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 890497b0d2..efdd2165aa 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -4,6 +4,7 @@ #include "block/block.h" #include "qapi/qapi-types-misc.h" #include "qapi/qapi-emit-events.h" +#include "qemu/coroutine-core.h" #include "qemu/readline.h" #include "exec/hwaddr.h" #include "qom/object.h" From 880584428002db3b5db3c24d3e5e2d59da9f32cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 11:27:57 +0200 Subject: [PATCH 537/860] monitor: Reduce inclusion of 'qapi/qapi-emit-events.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "monitor/monitor.h" don't use anything declared in the generated "qapi/qapi-emit-events.h" header. However the "monitor/monitor-internal.h" do: 107 struct MonitorClass { ... 116 /* 117 * If non-NULL, the monitor is able to send event 118 * notifications back to the client 119 */ 120 void (*emit_event)(Monitor *mon, QAPIEvent event, QDict *qdict); ^^^^^^^^^ Move the header inclusion to "monitor/monitor-internal.h" to avoid including / re-exposing unnecessary declarations in the global "monitor/monitor.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-11-philmd@oss.qualcomm.com> --- include/monitor/monitor.h | 1 - monitor/monitor-internal.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index efdd2165aa..fa9f4a8a75 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -3,7 +3,6 @@ #include "block/block.h" #include "qapi/qapi-types-misc.h" -#include "qapi/qapi-emit-events.h" #include "qemu/coroutine-core.h" #include "qemu/readline.h" #include "exec/hwaddr.h" diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index 23829f32f9..bc0932c425 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -27,6 +27,7 @@ #include "chardev/char-fe.h" #include "monitor/monitor.h" +#include "qapi/qapi-emit-events.h" #include "qapi/qapi-types-control.h" #include "qapi/qapi-types-qom.h" #include "qapi/qmp-registry.h" From 2697e7199a61a87964df0f202144f80b1f29a8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 11:15:01 +0200 Subject: [PATCH 538/860] monitor: Remove unnecessary 'block/block.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nothing here requires declarations from "block/block.h" anymore. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-12-philmd@oss.qualcomm.com> --- include/monitor/monitor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index fa9f4a8a75..fd98ee2c71 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -1,7 +1,6 @@ #ifndef MONITOR_H #define MONITOR_H -#include "block/block.h" #include "qapi/qapi-types-misc.h" #include "qemu/coroutine-core.h" #include "qemu/readline.h" From bd2871ac5031dde689ca42e2c3e4492da403d853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 10:40:56 +0200 Subject: [PATCH 539/860] system: Remove unnecessary 'monitor/monitor.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No code in device_tree.c or physmem.c require declarations from "monitor/monitor.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-13-philmd@oss.qualcomm.com> --- system/device_tree.c | 1 - system/physmem.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/system/device_tree.c b/system/device_tree.c index 1ea1962984..48365435ea 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -29,7 +29,6 @@ #include "qemu/config-file.h" #include "qapi/qapi-commands-machine.h" #include "qobject/qdict.h" -#include "monitor/hmp.h" #include diff --git a/system/physmem.c b/system/physmem.c index 2f37cbeb07..362a00f76c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -84,8 +84,6 @@ #include "qemu/mmap-alloc.h" #endif -#include "monitor/monitor.h" - #ifdef CONFIG_LIBDAXCTL #include #endif From c339d8459e9dce4d1ccd1e8f636138c348d00f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 10:39:23 +0200 Subject: [PATCH 540/860] system/dirtylimit: Extract HMP code to dirtylimit-hmp-cmds.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Reviewed-by: Hyman Huang Message-Id: <20260812211708.92824-14-philmd@oss.qualcomm.com> --- MAINTAINERS | 1 + system/dirtylimit-hmp-cmds.c | 74 ++++++++++++++++++++++++++++++++++++ system/dirtylimit.c | 60 ----------------------------- system/meson.build | 1 + 4 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 system/dirtylimit-hmp-cmds.c diff --git a/MAINTAINERS b/MAINTAINERS index c296dd5abf..bad65aeda5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3875,6 +3875,7 @@ Migration dirty limit and dirty page rate M: Hyman Huang S: Maintained F: system/dirtylimit.c +F: system/dirtylimit-hmp-cmds.c F: include/system/dirtylimit.h F: migration/dirtyrate.c F: migration/dirtyrate.h diff --git a/system/dirtylimit-hmp-cmds.c b/system/dirtylimit-hmp-cmds.c new file mode 100644 index 0000000000..4928d57cc8 --- /dev/null +++ b/system/dirtylimit-hmp-cmds.c @@ -0,0 +1,74 @@ +/* + * HMP commands related to migration dirty page rate limit + * + * Copyright (c) 2022 CHINA TELECOM CO.,LTD. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-migration.h" +#include "qobject/qdict.h" +#include "monitor/hmp.h" +#include "monitor/monitor.h" +#include "system/dirtylimit.h" + +void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + qmp_cancel_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " + "dirty limit for virtual CPU]\n"); +} + +void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + int64_t dirty_rate = qdict_get_int(qdict, "dirty_rate"); + int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); + Error *err = NULL; + + if (dirty_rate < 0) { + error_setg(&err, "invalid dirty page limit %" PRId64, dirty_rate); + goto out; + } + + qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err); + +out: + hmp_handle_error(mon, err); +} + +void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) +{ + DirtyLimitInfoList *info; + g_autoptr(DirtyLimitInfoList) head = NULL; + Error *err = NULL; + + if (!dirtylimit_in_service()) { + monitor_printf(mon, "Dirty page limit not enabled!\n"); + return; + } + + head = qmp_query_vcpu_dirty_limit(&err); + if (err) { + hmp_handle_error(mon, err); + return; + } + + for (info = head; info != NULL; info = info->next) { + monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)," + " current rate %"PRIi64 " (MB/s)\n", + info->value->cpu_index, + info->value->limit_rate, + info->value->current_rate); + } +} diff --git a/system/dirtylimit.c b/system/dirtylimit.c index 50fa67f3d6..70bb7bac2d 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -17,8 +17,6 @@ #include "qapi/error.h" #include "system/dirtyrate.h" #include "system/dirtylimit.h" -#include "monitor/hmp.h" -#include "monitor/monitor.h" #include "system/memory.h" #include "exec/target_page.h" #include "hw/core/boards.h" @@ -491,21 +489,6 @@ void qmp_cancel_vcpu_dirty_limit(bool has_cpu_index, dirtylimit_state_unlock(); } -void hmp_cancel_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) -{ - int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); - Error *err = NULL; - - qmp_cancel_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, &err); - if (err) { - hmp_handle_error(mon, err); - return; - } - - monitor_printf(mon, "[Please use 'info vcpu_dirty_limit' to query " - "dirty limit for virtual CPU]\n"); -} - void qmp_set_vcpu_dirty_limit(bool has_cpu_index, int64_t cpu_index, uint64_t dirty_rate, @@ -548,23 +531,6 @@ void qmp_set_vcpu_dirty_limit(bool has_cpu_index, dirtylimit_state_unlock(); } -void hmp_set_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) -{ - int64_t dirty_rate = qdict_get_int(qdict, "dirty_rate"); - int64_t cpu_index = qdict_get_try_int(qdict, "cpu_index", -1); - Error *err = NULL; - - if (dirty_rate < 0) { - error_setg(&err, "invalid dirty page limit %" PRId64, dirty_rate); - goto out; - } - - qmp_set_vcpu_dirty_limit(!!(cpu_index != -1), cpu_index, dirty_rate, &err); - -out: - hmp_handle_error(mon, err); -} - /* Return the max throttle time of each virtual CPU */ uint64_t dirtylimit_throttle_time_per_round(void) { @@ -646,29 +612,3 @@ struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp) { return dirtylimit_query_all(); } - -void hmp_info_vcpu_dirty_limit(Monitor *mon, const QDict *qdict) -{ - DirtyLimitInfoList *info; - g_autoptr(DirtyLimitInfoList) head = NULL; - Error *err = NULL; - - if (!dirtylimit_in_service()) { - monitor_printf(mon, "Dirty page limit not enabled!\n"); - return; - } - - head = qmp_query_vcpu_dirty_limit(&err); - if (err) { - hmp_handle_error(mon, err); - return; - } - - for (info = head; info != NULL; info = info->next) { - monitor_printf(mon, "vcpu[%"PRIi64"], limit rate %"PRIi64 " (MB/s)," - " current rate %"PRIi64 " (MB/s)\n", - info->value->cpu_index, - info->value->limit_rate, - info->value->current_rate); - } -} diff --git a/system/meson.build b/system/meson.build index cd3193d170..377adce803 100644 --- a/system/meson.build +++ b/system/meson.build @@ -9,6 +9,7 @@ system_ss.add(files( 'cpus.c', 'cpu-timers.c', 'dirtylimit.c', + 'dirtylimit-hmp-cmds.c', 'dma-helpers.c', 'exit-with-parent.c', 'globals.c', From 5bd649ccf93a6daa26888015deb6bfbc6d764d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 14:45:03 +0200 Subject: [PATCH 541/860] system: Move qmp_inject_nmi() to hw/core/machine-qmp-cmds.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We figured NMI relates to machines (for their machine-specific handling), so move the 'inject-nmi' QMP handler with the rest of machine ones, in hw/core/machine-qmp-cmds.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-15-philmd@oss.qualcomm.com> --- hw/core/machine-qmp-cmds.c | 6 ++++++ system/cpus.c | 8 -------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index e62cb4ec88..1aa5f26229 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "hw/acpi/vmgenid.h" #include "hw/core/boards.h" +#include "hw/core/nmi.h" #include "hw/intc/intc.h" #include "hw/mem/memory-device.h" #include "qapi/error.h" @@ -448,3 +449,8 @@ void qmp_dump_skeys(const char *filename, Error **errp) } DUMP_SKEYS_INTERFACE_CLASS(oc)->qmp_dump_skeys(filename, errp); } + +void qmp_inject_nmi(Error **errp) +{ + nmi_inject(errp); +} diff --git a/system/cpus.c b/system/cpus.c index 9758cda463..aa2510bf8f 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -26,7 +26,6 @@ #include "qemu/coroutine-tls.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-commands-misc.h" #include "qapi/qapi-events-run-state.h" #include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" @@ -38,7 +37,6 @@ #include "qemu/plugin.h" #include "system/cpus.h" #include "qemu/guest-random.h" -#include "hw/core/nmi.h" #include "system/physmem.h" #include "system/replay.h" #include "system/runstate.h" @@ -922,9 +920,3 @@ void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, exit: fclose(f); } - -void qmp_inject_nmi(Error **errp) -{ - nmi_inject(errp); -} - From 259c2aae8d24b7ef4631e11ed14b7b32795127f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 14:53:58 +0200 Subject: [PATCH 542/860] system: Extract QMP memsave/pmemsave commands to physmem-qmp-cmds.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep cpus.c related to vCPU scheduling, move the QMP handlers related to dumping physical memory to file to their own unit. Fix a pair of checkpatch.pl errors doing so: ERROR: braces {} are necessary for all arms of this statement #185: FILE: system/physmem-qmp-cmds.c:51: + if (l > size) [...] Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-16-philmd@oss.qualcomm.com> --- MAINTAINERS | 1 + system/cpus.c | 92 -------------------------------- system/meson.build | 1 + system/physmem-qmp-cmds.c | 107 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 92 deletions(-) create mode 100644 system/physmem-qmp-cmds.c diff --git a/MAINTAINERS b/MAINTAINERS index bad65aeda5..b51f5c3e60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3436,6 +3436,7 @@ F: system/ioport.c F: system/memory.c F: system/memory_mapping.c F: system/physmem.c +F: system/physmem-qmp-cmds.c F: system/memory_ldst* F: system/memory-internal.h F: system/ram-block-attributes.c diff --git a/system/cpus.c b/system/cpus.c index aa2510bf8f..43ff10cf00 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -25,9 +25,7 @@ #include "qemu/osdep.h" #include "qemu/coroutine-tls.h" #include "qapi/error.h" -#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-events-run-state.h" -#include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" #include "accel/accel-cpu-ops.h" #include "system/hw_accel.h" @@ -40,7 +38,6 @@ #include "system/physmem.h" #include "system/replay.h" #include "system/runstate.h" -#include "migration/misc.h" #include "system/cpu-timers.h" #include "system/whpx.h" #include "hw/core/boards.h" @@ -831,92 +828,3 @@ int vm_stop_force_state(RunState state) return ret; } } - -void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, - bool has_cpu, int64_t cpu_index, Error **errp) -{ - FILE *f; - uint64_t l; - CPUState *cpu; - uint8_t buf[1024]; - uint64_t orig_addr = addr, orig_size = size; - - if (migration_guest_ram_loading()) { - error_setg(errp, "Guest memory access not allowed during migration"); - return; - } - - if (!has_cpu) { - cpu_index = 0; - } - - cpu = qemu_get_cpu(cpu_index); - if (cpu == NULL) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", - "a CPU number"); - return; - } - - f = fopen(filename, "wb"); - if (!f) { - error_setg_file_open(errp, errno, filename); - return; - } - - while (size != 0) { - l = sizeof(buf); - if (l > size) - l = size; - if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { - error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 - " specified", orig_addr, orig_size); - goto exit; - } - if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, "writing memory to '%s' failed", - filename); - goto exit; - } - addr += l; - size -= l; - } - -exit: - fclose(f); -} - -void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, - Error **errp) -{ - FILE *f; - uint64_t l; - uint8_t buf[1024]; - - if (migration_guest_ram_loading()) { - error_setg(errp, "Guest memory access not allowed during migration"); - return; - } - - f = fopen(filename, "wb"); - if (!f) { - error_setg_file_open(errp, errno, filename); - return; - } - - while (size != 0) { - l = sizeof(buf); - if (l > size) - l = size; - physical_memory_read(addr, buf, l); - if (fwrite(buf, 1, l, f) != l) { - error_setg(errp, "writing memory to '%s' failed", - filename); - goto exit; - } - addr += l; - size -= l; - } - -exit: - fclose(f); -} diff --git a/system/meson.build b/system/meson.build index 377adce803..64e06e7abc 100644 --- a/system/meson.build +++ b/system/meson.build @@ -19,6 +19,7 @@ system_ss.add(files( 'memory_mapping.c', 'memory.c', 'physmem.c', + 'physmem-qmp-cmds.c', 'qdev-monitor.c', 'qtest.c', 'rtc.c', diff --git a/system/physmem-qmp-cmds.c b/system/physmem-qmp-cmds.c new file mode 100644 index 0000000000..d85430357d --- /dev/null +++ b/system/physmem-qmp-cmds.c @@ -0,0 +1,107 @@ +/* + * QMP commands to dump physical memory + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" +#include "qapi/qmp/qerror.h" +#include "hw/core/cpu.h" +#include "system/physmem.h" +#include "migration/misc.h" + +void qmp_memsave(uint64_t addr, uint64_t size, const char *filename, + bool has_cpu, int64_t cpu_index, Error **errp) +{ + FILE *f; + uint64_t l; + CPUState *cpu; + uint8_t buf[1024]; + uint64_t orig_addr = addr, orig_size = size; + + if (migration_guest_ram_loading()) { + error_setg(errp, "Guest memory access not allowed during migration"); + return; + } + + if (!has_cpu) { + cpu_index = 0; + } + + cpu = qemu_get_cpu(cpu_index); + if (cpu == NULL) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index", + "a CPU number"); + return; + } + + f = fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) { + l = size; + } + if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "/size %" PRIu64 + " specified", orig_addr, orig_size); + goto exit; + } + if (fwrite(buf, 1, l, f) != l) { + error_setg(errp, "writing memory to '%s' failed", + filename); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} + +void qmp_pmemsave(uint64_t addr, uint64_t size, const char *filename, + Error **errp) +{ + FILE *f; + uint64_t l; + uint8_t buf[1024]; + + if (migration_guest_ram_loading()) { + error_setg(errp, "Guest memory access not allowed during migration"); + return; + } + + f = fopen(filename, "wb"); + if (!f) { + error_setg_file_open(errp, errno, filename); + return; + } + + while (size != 0) { + l = sizeof(buf); + if (l > size) { + l = size; + } + physical_memory_read(addr, buf, l); + if (fwrite(buf, 1, l, f) != l) { + error_setg(errp, "writing memory to '%s' failed", + filename); + goto exit; + } + addr += l; + size -= l; + } + +exit: + fclose(f); +} From f036b28896f615eacbe70e89bc9966249c74b99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 10:41:57 +0200 Subject: [PATCH 543/860] system: Move runstate-related code from cpus.c to runstate.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep cpus.c focused on vCPUs handling, move code related to VM state to runstate.c where similar code lives. Fix few checkpatch.pl warnings: WARNING: Block comments use a leading /* on a separate line WARNING: Block comments use * on subsequent lines #327: FILE: system/runstate.c:541: +/* does a state transition even if the VM is already stopped, + current state is forgotten forever */ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-17-philmd@oss.qualcomm.com> --- system/cpus.c | 147 ------------------------------------------- system/runstate.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 147 deletions(-) diff --git a/system/cpus.c b/system/cpus.c index 43ff10cf00..f37e215c58 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "qemu/coroutine-tls.h" #include "qapi/error.h" -#include "qapi/qapi-events-run-state.h" #include "exec/gdbstub.h" #include "accel/accel-cpu-ops.h" #include "system/hw_accel.h" @@ -272,58 +271,6 @@ void cpu_interrupt(CPUState *cpu, int mask) cpus_accel->handle_interrupt(cpu, mask); } -/* - * True if the vm was previously suspended, and has not been woken or reset. - */ -static int vm_was_suspended; - -void vm_set_suspended(bool suspended) -{ - vm_was_suspended = suspended; -} - -bool vm_get_suspended(void) -{ - return vm_was_suspended; -} - -static int do_vm_stop(RunState state, bool send_stop) -{ - int ret = 0; - RunState oldstate = runstate_get(); - - if (runstate_is_live(oldstate)) { - vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED); - runstate_set(state); - cpu_disable_ticks(); - if (oldstate == RUN_STATE_RUNNING) { - pause_all_vcpus(); - } - ret = vm_state_notify(0, state); - if (send_stop) { - qapi_event_send_stop(); - } - } - - bdrv_drain_all(); - /* - * Even if vm_state_notify() return failure, - * it would be better to flush as before. - */ - ret |= bdrv_flush_all(); - trace_vm_stop_flush_all(ret); - - return ret; -} - -/* Special vm_stop() variant for terminating the process. Historically clients - * did not expect a QMP STOP event and so we need to retain compatibility. - */ -int vm_shutdown(void) -{ - return do_vm_stop(RUN_STATE_SHUTDOWN, false); -} - bool cpu_can_run(CPUState *cpu) { if (cpu->stop) { @@ -734,97 +681,3 @@ void cpu_stop_current(void) cpu_exit(current_cpu); } } - -int vm_stop(RunState state) -{ - if (qemu_in_vcpu_thread()) { - qemu_system_vmstop_request_prepare(); - qemu_system_vmstop_request(state); - /* - * FIXME: should not return to device code in case - * vm_stop() has been requested. - */ - cpu_stop_current(); - return 0; - } - - return do_vm_stop(state, true); -} - -/** - * Prepare for (re)starting the VM. - * Returns 0 if the vCPUs should be restarted, -1 on an error condition, - * and 1 otherwise. - */ -int vm_prepare_start(bool step_pending) -{ - int ret = vm_was_suspended ? 1 : 0; - RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING; - RunState requested; - - qemu_vmstop_requested(&requested); - if (runstate_is_running() && requested == RUN_STATE__MAX) { - return -1; - } - - /* Ensure that a STOP/RESUME pair of events is emitted if a - * vmstop request was pending. The BLOCK_IO_ERROR event, for - * example, according to documentation is always followed by - * the STOP event. - */ - if (runstate_is_running()) { - qapi_event_send_stop(); - qapi_event_send_resume(); - return -1; - } - - /* - * WHPX accelerator needs to know whether we are going to step - * any CPUs, before starting the first one. - */ - accel_pre_resume(MACHINE(qdev_get_machine()), step_pending); - - /* We are sending this now, but the CPUs will be resumed shortly later */ - qapi_event_send_resume(); - - cpu_enable_ticks(); - runstate_set(state); - vm_state_notify(1, state); - vm_was_suspended = false; - return ret; -} - -void vm_start(void) -{ - if (!vm_prepare_start(false)) { - resume_all_vcpus(); - } -} - -void vm_resume(RunState state) -{ - if (runstate_is_live(state)) { - vm_start(); - } else { - runstate_set(state); - } -} - -/* does a state transition even if the VM is already stopped, - current state is forgotten forever */ -int vm_stop_force_state(RunState state) -{ - if (runstate_is_live(runstate_get())) { - return vm_stop(state); - } else { - int ret; - runstate_set(state); - - bdrv_drain_all(); - /* Make sure to return an error if the flush in a previous vm_stop() - * failed. */ - ret = bdrv_flush_all(); - trace_vm_stop_flush_all(ret); - return ret; - } -} diff --git a/system/runstate.c b/system/runstate.c index 08acf801b0..d3e64d2b62 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -52,6 +52,7 @@ #include "qemu/thread.h" #include "qom/object.h" #include "qom/object_interfaces.h" +#include "system/cpu-timers.h" #include "system/cpus.h" #include "system/qtest.h" #include "system/replay.h" @@ -408,6 +409,159 @@ int vm_state_notify(bool running, RunState state) return ret; } +/* + * True if the vm was previously suspended, and has not been woken or reset. + */ +static int vm_was_suspended; + +void vm_set_suspended(bool suspended) +{ + vm_was_suspended = suspended; +} + +bool vm_get_suspended(void) +{ + return vm_was_suspended; +} + +static int do_vm_stop(RunState state, bool send_stop) +{ + int ret = 0; + RunState oldstate = runstate_get(); + + if (runstate_is_live(oldstate)) { + vm_was_suspended = (oldstate == RUN_STATE_SUSPENDED); + runstate_set(state); + cpu_disable_ticks(); + if (oldstate == RUN_STATE_RUNNING) { + pause_all_vcpus(); + } + ret = vm_state_notify(0, state); + if (send_stop) { + qapi_event_send_stop(); + } + } + + bdrv_drain_all(); + /* + * Even if vm_state_notify() return failure, + * it would be better to flush as before. + */ + ret |= bdrv_flush_all(); + trace_vm_stop_flush_all(ret); + + return ret; +} + +/* + * Special vm_stop() variant for terminating the process. Historically clients + * did not expect a QMP STOP event and so we need to retain compatibility. + */ +int vm_shutdown(void) +{ + return do_vm_stop(RUN_STATE_SHUTDOWN, false); +} + + +int vm_stop(RunState state) +{ + if (qemu_in_vcpu_thread()) { + qemu_system_vmstop_request_prepare(); + qemu_system_vmstop_request(state); + /* + * FIXME: should not return to device code in case + * vm_stop() has been requested. + */ + cpu_stop_current(); + return 0; + } + + return do_vm_stop(state, true); +} + +/** + * Prepare for (re)starting the VM. + * Returns 0 if the vCPUs should be restarted, -1 on an error condition, + * and 1 otherwise. + */ +int vm_prepare_start(bool step_pending) +{ + int ret = vm_was_suspended ? 1 : 0; + RunState state = vm_was_suspended ? RUN_STATE_SUSPENDED : RUN_STATE_RUNNING; + RunState requested; + + qemu_vmstop_requested(&requested); + if (runstate_is_running() && requested == RUN_STATE__MAX) { + return -1; + } + + /* + * Ensure that a STOP/RESUME pair of events is emitted if a + * vmstop request was pending. The BLOCK_IO_ERROR event, for + * example, according to documentation is always followed by + * the STOP event. + */ + if (runstate_is_running()) { + qapi_event_send_stop(); + qapi_event_send_resume(); + return -1; + } + + /* + * WHPX accelerator needs to know whether we are going to step + * any CPUs, before starting the first one. + */ + accel_pre_resume(MACHINE(qdev_get_machine()), step_pending); + + /* We are sending this now, but the CPUs will be resumed shortly later */ + qapi_event_send_resume(); + + cpu_enable_ticks(); + runstate_set(state); + vm_state_notify(1, state); + vm_was_suspended = false; + return ret; +} + +void vm_start(void) +{ + if (!vm_prepare_start(false)) { + resume_all_vcpus(); + } +} + +void vm_resume(RunState state) +{ + if (runstate_is_live(state)) { + vm_start(); + } else { + runstate_set(state); + } +} + +/* + * does a state transition even if the VM is already stopped, + * current state is forgotten forever + */ +int vm_stop_force_state(RunState state) +{ + if (runstate_is_live(runstate_get())) { + return vm_stop(state); + } else { + int ret; + runstate_set(state); + + bdrv_drain_all(); + /* + * Make sure to return an error if the flush in a previous vm_stop() + * failed. + */ + ret = bdrv_flush_all(); + trace_vm_stop_flush_all(ret); + return ret; + } +} + static ShutdownCause reset_requested; static ShutdownCause shutdown_requested; static int shutdown_exit_code = EXIT_SUCCESS; From e1212b51811bf95eaf384a9a97c481a2b888e03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 19:48:52 +0200 Subject: [PATCH 544/860] monitor: Rename MonitorHMP @mon -> @hmp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change to sanitize using the following patterns: MonitorQMP *qmp MonitorHMP *hmp Monitor *mon Rename @mon (and @hmp_mon) as @hmp when the type is MonitorHMP. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20260812211708.92824-19-philmd@oss.qualcomm.com> Acked-by: Marc-André Lureau --- include/monitor/monitor.h | 4 +- monitor/hmp-cmds.c | 6 +- monitor/hmp.c | 135 +++++++++++++++++++------------------ monitor/monitor-internal.h | 2 +- ui/ui-hmp-cmds.c | 4 +- 5 files changed, 76 insertions(+), 75 deletions(-) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index fd98ee2c71..54134f2aa4 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -55,8 +55,8 @@ void monitor_flush_locked(Monitor *mon); void *gpa2hva(MemoryRegion **p_mr, hwaddr addr, uint64_t size, Error **errp); -void monitor_read_command(MonitorHMP *mon, int show_prompt); -int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, +void monitor_read_command(MonitorHMP *hmp, int show_prompt); +int monitor_read_password(MonitorHMP *hmp, ReadLineFunc *readline_func, void *opaque); AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index e9fb8d827a..bcd0a1fc87 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -290,16 +290,16 @@ void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) void hmp_info_history(Monitor *mon, const QDict *qdict) { - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, parent_obj); + MonitorHMP *hmp = container_of(mon, MonitorHMP, parent_obj); int i; const char *str; - if (!hmp_mon->rs) { + if (!hmp->rs) { return; } i = 0; for(;;) { - str = readline_get_history(hmp_mon->rs, i); + str = readline_get_history(hmp->rs, i); if (!str) { break; } diff --git a/monitor/hmp.c b/monitor/hmp.c index 71a1888249..cbf7a114c0 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -49,24 +49,24 @@ OBJECT_DEFINE_TYPE(MonitorHMP, monitor_hmp, MONITOR_HMP, MONITOR); static void monitor_hmp_finalize(Object *obj) { - MonitorHMP *mon = MONITOR_HMP(obj); - if (mon->rs) { - readline_free(mon->rs); + MonitorHMP *hmp = MONITOR_HMP(obj); + if (hmp->rs) { + readline_free(hmp->rs); } } static bool monitor_hmp_get_readline(Object *obj, Error **errp) { - MonitorHMP *mon = MONITOR_HMP(obj); + MonitorHMP *hmp = MONITOR_HMP(obj); - return mon->use_readline; + return hmp->use_readline; } static void monitor_hmp_set_readline(Object *obj, bool val, Error **errp) { - MonitorHMP *mon = MONITOR_HMP(obj); + MonitorHMP *hmp = MONITOR_HMP(obj); - mon->use_readline = val; + hmp->use_readline = val; } int monitor_hmp_vprintf(Monitor *mon, const char *fmt, va_list ap) @@ -128,34 +128,34 @@ static void monitor_hmp_accept_input(Monitor *mon) static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque) { - MonitorHMP *mon = opaque; + MonitorHMP *hmp = opaque; - monitor_suspend(&mon->parent_obj); - handle_hmp_command(mon, cmdline); - monitor_resume(&mon->parent_obj); + monitor_suspend(&hmp->parent_obj); + handle_hmp_command(hmp, cmdline); + monitor_resume(&hmp->parent_obj); } -void monitor_read_command(MonitorHMP *mon, int show_prompt) +void monitor_read_command(MonitorHMP *hmp, int show_prompt) { - if (!mon->rs) { + if (!hmp->rs) { return; } - readline_start(mon->rs, "(qemu) ", 0, monitor_command_cb, NULL); + readline_start(hmp->rs, "(qemu) ", 0, monitor_command_cb, NULL); if (show_prompt) { - readline_show_prompt(mon->rs); + readline_show_prompt(hmp->rs); } } -int monitor_read_password(MonitorHMP *mon, ReadLineFunc *readline_func, +int monitor_read_password(MonitorHMP *hmp, ReadLineFunc *readline_func, void *opaque) { - if (mon->rs) { - readline_start(mon->rs, "Password: ", 1, readline_func, opaque); + if (hmp->rs) { + readline_start(hmp->rs, "Password: ", 1, readline_func, opaque); /* prompt is printed on return from the command handler */ return 0; } else { - monitor_printf(&mon->parent_obj, + monitor_printf(&hmp->parent_obj, "terminal does not support password prompting\n"); return -ENOTTY; } @@ -772,12 +772,12 @@ static const HMPCommand *search_dispatch_table(const HMPCommand *disp_table, * Do not assume the return value points into @table! It doesn't when * the command is found in a sub-command table. */ -static const HMPCommand *monitor_parse_command(MonitorHMP *hmp_mon, +static const HMPCommand *monitor_parse_command(MonitorHMP *hmp, const char *cmdp_start, const char **cmdp, HMPCommand *table) { - Monitor *mon = &hmp_mon->parent_obj; + Monitor *mon = &hmp->parent_obj; const char *p; const HMPCommand *cmd; char cmdname[256]; @@ -809,7 +809,7 @@ static const HMPCommand *monitor_parse_command(MonitorHMP *hmp_mon, *cmdp = p; /* search sub command */ if (cmd->sub_table != NULL && *p != '\0') { - return monitor_parse_command(hmp_mon, cmdp_start, cmdp, cmd->sub_table); + return monitor_parse_command(hmp, cmdp_start, cmdp, cmd->sub_table); } return cmd; @@ -1254,15 +1254,15 @@ static void handle_hmp_command_co(void *opaque) data->done = true; } -void handle_hmp_command(MonitorHMP *mon, const char *cmdline) +void handle_hmp_command(MonitorHMP *hmp, const char *cmdline) { QDict *qdict; const HMPCommand *cmd; const char *cmd_start = cmdline; - trace_handle_hmp_command(mon, cmdline); + trace_handle_hmp_command(hmp, cmdline); - cmd = monitor_parse_command(mon, cmdline, &cmdline, + cmd = monitor_parse_command(hmp, cmdline, &cmdline, hmp_cmds_for_target(false)); if (!cmd) { return; @@ -1270,17 +1270,17 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) if (!cmd->cmd && !cmd->cmd_info_hrt) { /* FIXME: is it useful to try autoload modules here ??? */ - monitor_printf(&mon->parent_obj, "Command \"%.*s\" is not available.\n", + monitor_printf(&hmp->parent_obj, "Command \"%.*s\" is not available.\n", (int)(cmdline - cmd_start), cmd_start); return; } - qdict = monitor_parse_arguments(&mon->parent_obj, &cmdline, cmd); + qdict = monitor_parse_arguments(&hmp->parent_obj, &cmdline, cmd); if (!qdict) { while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) { cmdline--; } - monitor_printf(&mon->parent_obj, + monitor_printf(&hmp->parent_obj, "Try \"help %.*s\" for more information\n", (int)(cmdline - cmd_start), cmd_start); return; @@ -1289,18 +1289,18 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) if (!cmd->coroutine) { /* old_mon is non-NULL when called from qmp_human_monitor_command() */ Monitor *old_mon = monitor_set_cur(qemu_coroutine_self(), - &mon->parent_obj); - handle_hmp_command_exec(&mon->parent_obj, cmd, qdict); + &hmp->parent_obj); + handle_hmp_command_exec(&hmp->parent_obj, cmd, qdict); monitor_set_cur(qemu_coroutine_self(), old_mon); } else { HandleHmpCommandCo data = { - .mon = &mon->parent_obj, + .mon = &hmp->parent_obj, .cmd = cmd, .qdict = qdict, .done = false, }; Coroutine *co = qemu_coroutine_create(handle_hmp_command_co, &data); - monitor_set_cur(co, &mon->parent_obj); + monitor_set_cur(co, &hmp->parent_obj); aio_co_enter(qemu_get_aio_context(), co); AIO_WAIT_WHILE_UNLOCKED(NULL, !data.done); } @@ -1308,7 +1308,8 @@ void handle_hmp_command(MonitorHMP *mon, const char *cmdline) qobject_unref(qdict); } -static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) +static void cmd_completion(MonitorHMP *hmp, + const char *name, const char *list) { const char *p, *pstart; char cmd[128]; @@ -1324,7 +1325,7 @@ static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) } memcpy(cmd, pstart, len); cmd[len] = '\0'; - readline_add_completion_of(mon->rs, name, cmd); + readline_add_completion_of(hmp->rs, name, cmd); if (*p == '\0') { break; } @@ -1332,7 +1333,7 @@ static void cmd_completion(MonitorHMP *mon, const char *name, const char *list) } } -static void file_completion(MonitorHMP *mon, const char *input) +static void file_completion(MonitorHMP *hmp, const char *input) { DIR *ffs; struct dirent *d; @@ -1384,7 +1385,7 @@ static void file_completion(MonitorHMP *mon, const char *input) if (stat(file, &sb) == 0 && S_ISDIR(sb.st_mode)) { pstrcat(file, sizeof(file), "/"); } - readline_add_completion(mon->rs, file); + readline_add_completion(hmp->rs, file); } } closedir(ffs); @@ -1396,7 +1397,7 @@ static const char *next_arg_type(const char *typestr) return (p != NULL ? ++p : typestr); } -static void monitor_find_completion_by_table(MonitorHMP *mon, +static void monitor_find_completion_by_table(MonitorHMP *hmp, const HMPCommand *cmd_table, char **args, int nb_args) @@ -1414,10 +1415,10 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, } else { cmdname = args[0]; } - readline_set_completion_index(mon->rs, strlen(cmdname)); + readline_set_completion_index(hmp->rs, strlen(cmdname)); for (cmd = cmd_table; cmd->name != NULL; cmd++) { if (cmd_available(cmd)) { - cmd_completion(mon, cmdname, cmd->name); + cmd_completion(hmp, cmdname, cmd->name); } } } else { @@ -1434,12 +1435,12 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, if (cmd->sub_table) { /* do the job again */ - monitor_find_completion_by_table(mon, cmd->sub_table, + monitor_find_completion_by_table(hmp, cmd->sub_table, &args[1], nb_args - 1); return; } if (cmd->command_completion) { - cmd->command_completion(mon->rs, nb_args, args[nb_args - 1]); + cmd->command_completion(hmp->rs, nb_args, args[nb_args - 1]); return; } @@ -1461,20 +1462,20 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, switch (*ptype) { case 'F': /* file completion */ - readline_set_completion_index(mon->rs, strlen(str)); - file_completion(mon, str); + readline_set_completion_index(hmp->rs, strlen(str)); + file_completion(hmp, str); break; case 'B': /* block device name completion */ - readline_set_completion_index(mon->rs, strlen(str)); + readline_set_completion_index(hmp->rs, strlen(str)); while ((blk = blk_next(blk)) != NULL) { - readline_add_completion_of(mon->rs, str, blk_name(blk)); + readline_add_completion_of(hmp->rs, str, blk_name(blk)); } break; case 's': case 'S': if (!strcmp(cmd->name, "help|?")) { - monitor_find_completion_by_table(mon, cmd_table, + monitor_find_completion_by_table(hmp, cmd_table, &args[1], nb_args - 1); } break; @@ -1487,7 +1488,7 @@ static void monitor_find_completion_by_table(MonitorHMP *mon, static void monitor_find_completion(void *opaque, const char *cmdline) { - MonitorHMP *mon = opaque; + MonitorHMP *hmp = opaque; char *args[MAX_ARGS]; int nb_args, len; @@ -1509,7 +1510,7 @@ static void monitor_find_completion(void *opaque, } /* 2. auto complete according to args */ - monitor_find_completion_by_table(mon, hmp_cmds_for_target(false), + monitor_find_completion_by_table(hmp, hmp_cmds_for_target(false), args, nb_args); cleanup: @@ -1518,18 +1519,18 @@ cleanup: static void monitor_read(void *opaque, const uint8_t *buf, int size) { - MonitorHMP *mon = container_of(opaque, MonitorHMP, parent_obj); + MonitorHMP *hmp = container_of(opaque, MonitorHMP, parent_obj); int i; - if (mon->rs) { + if (hmp->rs) { for (i = 0; i < size; i++) { - readline_handle_byte(mon->rs, buf[i]); + readline_handle_byte(hmp->rs, buf[i]); } } else { if (size == 0 || buf[size - 1] != 0) { - monitor_printf(&mon->parent_obj, "corrupted command\n"); + monitor_printf(&hmp->parent_obj, "corrupted command\n"); } else { - handle_hmp_command(mon, (char *)buf); + handle_hmp_command(hmp, (char *)buf); } } } @@ -1598,17 +1599,17 @@ static void monitor_event(void *opaque, QEMUChrEvent event) static void G_GNUC_PRINTF(2, 3) monitor_readline_printf(void *opaque, const char *fmt, ...) { - MonitorHMP *mon = opaque; + MonitorHMP *hmp = opaque; va_list ap; va_start(ap, fmt); - monitor_vprintf(&mon->parent_obj, fmt, ap); + monitor_vprintf(&hmp->parent_obj, fmt, ap); va_end(ap); } static void monitor_readline_flush(void *opaque) { - MonitorHMP *mon = opaque; - monitor_flush(&mon->parent_obj); + MonitorHMP *hmp = opaque; + monitor_flush(&hmp->parent_obj); } void monitor_new_hmp(const char *id, const char *chardev_id, @@ -1626,11 +1627,11 @@ void monitor_new_hmp(const char *id, const char *chardev_id, static void monitor_hmp_complete(UserCreatable *uc, Error **errp) { - MonitorHMP *mon = MONITOR_HMP(uc); + MonitorHMP *hmp = MONITOR_HMP(uc); UserCreatableClass *ucc_parent = USER_CREATABLE_CLASS( object_class_get_parent( - OBJECT_CLASS(MONITOR_HMP_GET_CLASS(mon)))); + OBJECT_CLASS(MONITOR_HMP_GET_CLASS(hmp)))); ERRP_GUARD(); ucc_parent->complete(uc, errp); @@ -1638,21 +1639,21 @@ static void monitor_hmp_complete(UserCreatable *uc, Error **errp) return; } - if (mon->parent_obj.chardev_id) { - if (mon->use_readline) { - mon->rs = readline_init(monitor_readline_printf, + if (hmp->parent_obj.chardev_id) { + if (hmp->use_readline) { + hmp->rs = readline_init(monitor_readline_printf, monitor_readline_flush, - mon, + hmp, monitor_find_completion); - monitor_read_command(mon, 0); + monitor_read_command(hmp, 0); } - qemu_chr_fe_set_handlers(&mon->parent_obj.chr, + qemu_chr_fe_set_handlers(&hmp->parent_obj.chr, monitor_can_read, monitor_read, monitor_event, NULL, - &mon->parent_obj, NULL, true); - monitor_list_append(&mon->parent_obj); + &hmp->parent_obj, NULL, true); + monitor_list_append(&hmp->parent_obj); } } diff --git a/monitor/monitor-internal.h b/monitor/monitor-internal.h index bc0932c425..e0a124a317 100644 --- a/monitor/monitor-internal.h +++ b/monitor/monitor-internal.h @@ -221,7 +221,7 @@ void monitor_data_destroy_qmp(MonitorQMP *mon); void coroutine_fn monitor_qmp_dispatcher_co(void *data); void qmp_dispatcher_co_wake(void); -void handle_hmp_command(MonitorHMP *mon, const char *cmdline); +void handle_hmp_command(MonitorHMP *hmp, const char *cmdline); int hmp_compare_cmd(const char *name, const char *list); /* diff --git a/ui/ui-hmp-cmds.c b/ui/ui-hmp-cmds.c index 06f4030ce4..24cf3a6710 100644 --- a/ui/ui-hmp-cmds.c +++ b/ui/ui-hmp-cmds.c @@ -343,8 +343,8 @@ void hmp_change_vnc(Monitor *mon, const char *device, const char *target, return; } if (!arg) { - MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, parent_obj); - monitor_read_password(hmp_mon, hmp_change_read_arg, NULL); + MonitorHMP *hmp = container_of(mon, MonitorHMP, parent_obj); + monitor_read_password(hmp, hmp_change_read_arg, NULL); } else { qmp_change_vnc_password(arg, errp); } From e33c5ac0d8ce64b9e08d7309ea5a3448edb3a13e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 22:39:29 +0200 Subject: [PATCH 545/860] monitor: Better express monitor_read()'s opaque arg is of Monitor type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit monitor_read() is a IOReadHandler handler, called by qemu_chr_fe_set_handlers() with a Monitor* opaque argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-20-philmd@oss.qualcomm.com> --- monitor/hmp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monitor/hmp.c b/monitor/hmp.c index cbf7a114c0..46f8d4a6ed 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -1519,7 +1519,8 @@ cleanup: static void monitor_read(void *opaque, const uint8_t *buf, int size) { - MonitorHMP *hmp = container_of(opaque, MonitorHMP, parent_obj); + Monitor *mon = opaque; + MonitorHMP *hmp = container_of(mon, MonitorHMP, parent_obj); int i; if (hmp->rs) { From f32f9ffcfec61face1b9a684d19f64cc2cbcd6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Aug 2026 19:33:08 +0200 Subject: [PATCH 546/860] monitor: Replace container_of(MonitorHMP, parent_obj) -> MONITOR_HMP() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By replacing the container_of(MonitorHMP) use in ui/ui-hmp-cmds.c we can remove its incorrect inclusion of "monitor/monitor-internal.h" header, using the public "monitor/monitor.h" instead. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Message-Id: <20260812211708.92824-23-philmd@oss.qualcomm.com> --- monitor/hmp-cmds.c | 2 +- monitor/hmp.c | 2 +- ui/ui-hmp-cmds.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index bcd0a1fc87..4e8d996dbb 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -290,7 +290,7 @@ void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) void hmp_info_history(Monitor *mon, const QDict *qdict) { - MonitorHMP *hmp = container_of(mon, MonitorHMP, parent_obj); + MonitorHMP *hmp = MONITOR_HMP(mon); int i; const char *str; diff --git a/monitor/hmp.c b/monitor/hmp.c index 46f8d4a6ed..22b1972d34 100644 --- a/monitor/hmp.c +++ b/monitor/hmp.c @@ -1520,7 +1520,7 @@ cleanup: static void monitor_read(void *opaque, const uint8_t *buf, int size) { Monitor *mon = opaque; - MonitorHMP *hmp = container_of(mon, MonitorHMP, parent_obj); + MonitorHMP *hmp = MONITOR_HMP(mon); int i; if (hmp->rs) { diff --git a/ui/ui-hmp-cmds.c b/ui/ui-hmp-cmds.c index 24cf3a6710..806a7bece7 100644 --- a/ui/ui-hmp-cmds.c +++ b/ui/ui-hmp-cmds.c @@ -19,7 +19,7 @@ #endif #include "monitor/hmp.h" #include "monitor/hmp-completion.h" -#include "monitor/monitor-internal.h" +#include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "qobject/qdict.h" @@ -343,7 +343,7 @@ void hmp_change_vnc(Monitor *mon, const char *device, const char *target, return; } if (!arg) { - MonitorHMP *hmp = container_of(mon, MonitorHMP, parent_obj); + MonitorHMP *hmp = MONITOR_HMP(mon); monitor_read_password(hmp, hmp_change_read_arg, NULL); } else { qmp_change_vnc_password(arg, errp); From 260d49d8e28378b1f4f6c255c15cc20617726920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Wed, 12 Aug 2026 09:14:05 +0100 Subject: [PATCH 547/860] hw/elf_ops: defend against weird elf headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the ELF spec: PT_LOAD The array element specifies a loadable segment, described by p_filesz and p_memsz. The bytes from the file are mapped to the beginning of the memory segment. If the segment's memory size (p_memsz) is larger than the file size (p_filesz), the ``extra'' bytes are defined to hold the value 0 and to follow the segment's initialized area. The file size may not be larger than the memory size. Loadable segment entries in the program header table appear in ascending order, sorted on the p_vaddr member. which implies while both p_filesz and p_memsz can be zero we should never see a case where p_filesz is greater than the in memory size. Indeed it has been reported such a hand crafted ELF can blow up, for example during rom_reset(): address_space_set(rom->as, rom->addr + rom->datasize, 0, rom->romsize - rom->datasize, MEMTXATTRS_UNSPECIFIED); which could trigger and underflow leaving QEMU slowly filling a very large buffer. Cc: qemu-stable@nongnu.org Fixes: https://gitlab.com/qemu-project/qemu/-/work_items/4056 Signed-off-by: Alex Bennée Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260812081405.3811787-1-alex.bennee@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/elf_ops.h.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/hw/elf_ops.h.inc b/include/hw/elf_ops.h.inc index 9c35d1b9da..044e72de2a 100644 --- a/include/hw/elf_ops.h.inc +++ b/include/hw/elf_ops.h.inc @@ -427,6 +427,11 @@ static ssize_t glue(load_elf, SZ)(const char *name, int fd, file_size = ph->p_filesz; /* Size of the allocated data */ data_offset = ph->p_offset; /* Offset where the data is located */ + if (file_size > mem_size) { + ret = ELF_LOAD_TOO_BIG; + goto fail; + } + if (file_size > 0) { if (g_mapped_file_get_length(mapped_file) < file_size + data_offset) { From b409523587c49a08b1ddadd51212a320092347b1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 14 Aug 2026 03:26:00 +0000 Subject: [PATCH 548/860] hw/core/machine: Move EHCI migration compat properties to 11.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The x-migrate-fetch-addr-64bit compatibility properties for sysbus-ehci-usb and pci-ehci-usb were reviewed before the QEMU 11.1 release and were therefore initially added to hw_compat_11_0. However, the EHCI migration change was merged after the QEMU 11.1 release. As a result, these compatibility properties belong in hw_compat_11_1 rather than hw_compat_11_0. Move both properties to hw_compat_11_1 so that migration compatibility is associated with the correct machine version. Fixes: 38ed803aebb2 ("usb/hcd-ehci: Change descriptor addresses to 64-bit") Signed-off-by: Jamin Lin Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260814032559.3381363-1-jamin_lin@aspeedtech.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/machine.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 4e55119446..e617f7804d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -40,7 +40,10 @@ #include "qemu/audio.h" #include "hw/arm/smmuv3.h" -GlobalProperty hw_compat_11_1[] = {}; +GlobalProperty hw_compat_11_1[] = { + { "sysbus-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, + { "pci-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, +}; const size_t hw_compat_11_1_len = G_N_ELEMENTS(hw_compat_11_1); GlobalProperty hw_compat_11_0[] = { @@ -54,8 +57,6 @@ GlobalProperty hw_compat_11_0[] = { { TYPE_ARM_SMMUV3, "ssidsize", "0" }, { TYPE_ARM_SMMUV3, "oas", "44" }, { "migration", "switchover-ack-legacy", "on" }, - { "sysbus-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, - { "pci-ehci-usb", "x-migrate-fetch-addr-64bit", "off" }, }; const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0); From 0924d9d3db3617f49ba9282acc6d85b349404ce6 Mon Sep 17 00:00:00 2001 From: Simon Scherer Date: Fri, 7 Aug 2026 08:28:31 +0200 Subject: [PATCH 549/860] target/i386: Clear OF, SF, and AF for fcomi/fucomi helper_fcomi_ST0_FT0() and helper_fucomi_ST0_FT0() only cleared CC_Z, CC_P, and CC_C before merging in the comparison result, leaving CC_O, CC_S, and CC_A untouched from whatever they were set to beforehand. The Intel SDM documents FCOMI/FCOMIP/FUCOMI/FUCOMIP as setting OF, SF, and AF to 0 unconditionally. The AMD manual doesn't mention them at all. However, testing on multiple real Intel and AMD systems confirms all three are unconditionally cleared regardless of the comparison result or their prior value. Since fcomi_ccval[] only ever contains CC_C, CC_Z, 0, or CC_Z|CC_P|CC_C, and CC_O|CC_S|CC_Z|CC_A|CC_P|CC_C already covers every flag bit, CC_SRC can be assigned from fcomi_ccval[ret + 1] directly instead of ORing it into a masked cpu_cc_compute_all() result. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4133 Signed-off-by: Simon Scherer Link: https://lore.kernel.org/r/20260807062831.19618-1-scherer.simon89@gmail.com Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/fpu_helper.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 978eb1411b..b812125efa 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -550,12 +550,11 @@ static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; void helper_fcomi_ST0_FT0(CPUX86State *env) { int old_flags = save_exception_flags(env); - int eflags; FloatRelation ret; ret = floatx80_compare(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); - CC_SRC = eflags | fcomi_ccval[ret + 1]; + /* OF, SF, and AF are unconditionally cleared to 0 */ + CC_SRC = fcomi_ccval[ret + 1]; CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } @@ -563,12 +562,11 @@ void helper_fcomi_ST0_FT0(CPUX86State *env) void helper_fucomi_ST0_FT0(CPUX86State *env) { int old_flags = save_exception_flags(env); - int eflags; FloatRelation ret; ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env) & ~(CC_Z | CC_P | CC_C); - CC_SRC = eflags | fcomi_ccval[ret + 1]; + /* OF, SF, and AF are unconditionally cleared to 0 */ + CC_SRC = fcomi_ccval[ret + 1]; CC_OP = CC_OP_EFLAGS; merge_exception_flags(env, old_flags); } From 654d4cd059a541f56091dbfe9220806fd9b51a07 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Thu, 2 Jul 2026 14:47:46 +0200 Subject: [PATCH 550/860] target/i386: Skip supervisor in xsave decompaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supervisor state should be skipped b/c there is no slot in standard format XSAVE buffer for it. CET State is being migrated via MSRs and other supervisor state isn't currently migrated. Fixes: 8612deb3f4 Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260702124746.450228-1-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 ++ target/i386/xsave_helper.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e6a197602d..fff701c6c4 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -655,9 +655,11 @@ typedef enum X86Seg { #define XSTATE_DYNAMIC_MASK (XSTATE_XTILE_DATA_MASK) +#define ESA_FEATURE_XSS_BIT 0 #define ESA_FEATURE_ALIGN64_BIT 1 #define ESA_FEATURE_XFD_BIT 2 +#define ESA_FEATURE_XSS_MASK (1U << ESA_FEATURE_XSS_BIT) #define ESA_FEATURE_ALIGN64_MASK (1U << ESA_FEATURE_ALIGN64_BIT) #define ESA_FEATURE_XFD_MASK (1U << ESA_FEATURE_XFD_BIT) diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index 625bae103a..1fa3133b1a 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -332,7 +332,7 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) size_t i; uint32_t eax, ebx, ecx, edx; uint32_t size, dst_off; - bool align64; + bool align64, supervisor; uint64_t guest_xcr0, *xstate_bv; compacted_xstate_bv = *(uint64_t *)(buf + XSAVE_XSTATE_BV_OFFSET); @@ -383,6 +383,7 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) size = eax; dst_off = ebx; align64 = (ecx & (1u << 1)) != 0; + supervisor = (ecx & ESA_FEATURE_XSS_MASK) != 0; /* Component is in the layout but unknown to the guest CPUID model */ if (size == 0) { @@ -433,8 +434,14 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) return -E2BIG; } - /* Copy components marked present in XSTATE_BV to guest model */ - if (((compacted_xstate_bv >> i) & 1) != 0) { + /* + * Copy components marked present in XSTATE_BV to guest model. + * + * NB: Supervisor state is skipped b/c there is no slot in the + * standard format XSAVE buffer (CET state is migrated via MSRs, + * others supervisor state isn't migrated). + */ + if (((compacted_xstate_bv >> i) & 1) != 0 && !supervisor) { memcpy(env->xsave_buf + dst_off, buf + xsave_offset, size); } From 5cc3e4ea53567405a30241c69da6215124279c64 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 7 Aug 2026 15:19:03 +0200 Subject: [PATCH 551/860] target/i386: use ESA_FEATURE_ALIGN64_MASK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not hardcode bit 1. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- target/i386/xsave_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index 1fa3133b1a..666a281002 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -382,7 +382,7 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) size = eax; dst_off = ebx; - align64 = (ecx & (1u << 1)) != 0; + align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0; supervisor = (ecx & ESA_FEATURE_XSS_MASK) != 0; /* Component is in the layout but unknown to the guest CPUID model */ @@ -394,7 +394,7 @@ int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env) */ host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx); size = eax; - align64 = (ecx & (1u << 1)) != 0; + align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0; if (size == 0) { error_report("xsave component %zu: size unknown to both " "guest and host CPUID", i); @@ -530,7 +530,7 @@ int compact_xsave_area(CPUX86State *env, void *buf, size_t buflen) host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx); size = eax; src_off = ebx; - align64 = (ecx >> 1) & 1; + align64 = (ecx & ESA_FEATURE_ALIGN64_MASK) != 0; if (size == 0) { /* Component in host xcr0 but unknown - shouldn't happen */ From ce0ee66044be066cbbf076a7530fe4e8f638f847 Mon Sep 17 00:00:00 2001 From: Simon Scherer Date: Thu, 25 Jun 2026 17:56:13 +0200 Subject: [PATCH 552/860] target/i386: fix incorrect decoding of EXTRQ_i The decoding of the extrq instruction with an immediate operand (EXTRQ_i) is incorrect. Per the AMD manual the instruction encoding looks as follows: EXTRQ xmm1, imm8, imm8 66 0F 78 /0 ib ib The /0 indicates that the "Reg" field of the ModR/M byte must be equal to 0 and the XMM register operand is specified by the "R/M" field. However, qemu incorrectly uses the "Reg" field to extract the register operand. This patch instead extracts the XMM register operand from the "R/M" field. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3611 Signed-off-by: Simon Scherer Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20260625155613.192643-1-scherer.simon89@gmail.com [Check for the reg field to be 0. Make decoding of REPZ+66 consistent between 0F 78 and 0F 79. - Paolo] Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index ac181308ca..ac5964b95d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -603,23 +603,33 @@ static void decode_0F77(DisasContext *s, CPUX86State *env, X86OpEntry *entry, ui static void decode_0F78(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { - static const X86OpEntry opcodes_0F78[4] = { - {}, - X86_OP_ENTRY3(EXTRQ_i, V,x, None,None, I,w, cpuid(SSE4A)), /* AMD extension */ - {}, - X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)), /* AMD extension */ - }; - *entry = *decode_by_prefix(s, opcodes_0F78); + static const X86OpEntry opcodes_0F78_f2 = + X86_OP_ENTRY3(INSERTQ_i, V,x, U,x, I,w, cpuid(SSE4A)); /* AMD extension */ + static const X86OpEntry opcodes_0F78_66 = + X86_OP_ENTRY3(EXTRQ_i, U,x, None,None, I,w, cpuid(SSE4A)); /* AMD extension */ + + entry->gen = NULL; + if (s->prefix & PREFIX_REPNZ) { + *entry = opcodes_0F78_f2; + } else if (s->prefix & PREFIX_REPZ) { + /* undefined */ + } else if (s->prefix & PREFIX_DATA) { + int op = (get_modrm(s, env) >> 3) & 7; + if (op == 0) { + *entry = opcodes_0F78_66; + } + } } static void decode_0F79(DisasContext *s, CPUX86State *env, X86OpEntry *entry, uint8_t *b) { + entry->gen = NULL; if (s->prefix & PREFIX_REPNZ) { entry->gen = gen_INSERTQ_r; /* AMD extension */ + } else if (s->prefix & PREFIX_REPZ) { + /* undefined */ } else if (s->prefix & PREFIX_DATA) { entry->gen = gen_EXTRQ_r; /* AMD extension */ - } else { - entry->gen = NULL; }; } From 3589cd995b4facf34071e944fd8ec2294524e25a Mon Sep 17 00:00:00 2001 From: "Craaijo, Jos" Date: Tue, 23 Jun 2026 13:21:22 +0000 Subject: [PATCH 553/860] target/i386: fix long mode segment override prefix decoding On x86, the ES/CS/SS/DS segment override prefixes are null prefixes in long mode, and should be ignored. (AMD APM Volume 3, Section 1.2.4) This patch fixes the prefix decoding to correctly ignore the prefixes in 64-bit mode. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3391 Signed-off-by: Jos Craaijo Tested-by: Yudistira Putra Link: https://lore.kernel.org/r/20260623-fix-x86-long-mode-segment-override-decoding-v1-1-26d9d4b5804e@ou.nl Signed-off-by: Paolo Bonzini --- target/i386/emulate/x86_decode.c | 6 ++++++ target/i386/tcg/decode-new.c.inc | 16 ++++++++++++---- tests/tcg/x86_64/Makefile.target | 1 + tests/tcg/x86_64/segment-prefixes.c | 25 +++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 tests/tcg/x86_64/segment-prefixes.c diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c index bae1dd4d6f..34dcac155a 100644 --- a/target/i386/emulate/x86_decode.c +++ b/target/i386/emulate/x86_decode.c @@ -1851,6 +1851,12 @@ static void decode_prefix(CPUX86State *env, struct x86_decode *decode) case PREFIX_SS_SEG_OVERRIDE: case PREFIX_DS_SEG_OVERRIDE: case PREFIX_ES_SEG_OVERRIDE: + if (x86_is_long_mode(env_cpu(env))) { + /* ES/CS/SS/DS segment overrides are ignored in long mode */ + decode->rex.rex = 0; + break; + } + /* fall through when not in long mode */ case PREFIX_FS_SEG_OVERRIDE: case PREFIX_GS_SEG_OVERRIDE: decode->segment_override = byte; diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index ac5964b95d..faf74b00bb 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2816,16 +2816,24 @@ static void disas_insn(DisasContext *s, CPUState *cpu) s->prefix |= PREFIX_LOCK; goto next_byte; case 0x2e: - s->override = R_CS; + if (!CODE64(s)) { + s->override = R_CS; + } goto next_byte; case 0x36: - s->override = R_SS; + if (!CODE64(s)) { + s->override = R_SS; + } goto next_byte; case 0x3e: - s->override = R_DS; + if (!CODE64(s)) { + s->override = R_DS; + } goto next_byte; case 0x26: - s->override = R_ES; + if (!CODE64(s)) { + s->override = R_ES; + } goto next_byte; case 0x64: s->override = R_FS; diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index be20fc64e8..c48767fef8 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -15,6 +15,7 @@ X86_64_TESTS += vsyscall X86_64_TESTS += noexec X86_64_TESTS += cmpxchg X86_64_TESTS += adox +X86_64_TESTS += segment-prefixes X86_64_TESTS += test-1648 X86_64_TESTS += test-2175 X86_64_TESTS += cross-modifying-code diff --git a/tests/tcg/x86_64/segment-prefixes.c b/tests/tcg/x86_64/segment-prefixes.c new file mode 100644 index 0000000000..a7e6e285b2 --- /dev/null +++ b/tests/tcg/x86_64/segment-prefixes.c @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* See https://gitlab.com/qemu-project/qemu/-/work_items/3391 */ + +int main() +{ + int data = 0; + + /* Ensure that ignored segment override prefixes are actually ignored */ + asm volatile ( + "wrgsbase %0\n\t" + ".byte 0x65, 0x26\n\t" /* prefixes: GS + ES */ + "movb $0, 0\n\t" + ".byte 0x65, 0x2E\n\t" /* prefixes: GS + CS */ + "movb $0, 0\n\t" + ".byte 0x65, 0x36\n\t" /* prefixes: GS + SS */ + "movb $0, 0\n\t" + ".byte 0x65, 0x3E\n\t" /* prefixes: GS + DS */ + "movb $0, 0\n\t" + : + : "r" (&data) + : "memory" + ); + + return 0; +} From 36f634fe4ab6b266031164ab8d1160e6d11addda Mon Sep 17 00:00:00 2001 From: Andrey Polivoda Date: Mon, 22 Jun 2026 18:21:19 +1000 Subject: [PATCH 554/860] target/i386: allow transition to virtual-8086 mode only if CPL == 0 and CPU is not in long mode According to the pseudocode for the IRET instruction in both the Intel 64 and IA-32 Architectures Software Developer's Manual and the AMD64 Architecture Programmer's Manual, a transition to virtual-8086 mode is allowed only if all of the following conditions are met: 1. The new EFLAGS.VM bit is set to 1. 2. The Current Privilege Level (CPL) is 0. 3. The CPU is in protected mode (and not in long mode). Currently, QEMU performs only the first check. This omission allows a transition to virtual-8086 mode from long mode, and also enables the guest's userspace to trigger this switch. During a legitimate transition, the EFLAGS register is updated in a way that allows modification of sensitive fields, such as IOPL and IF (which is expected, as only privileged code should be able to initiate this transition). However, due to the lack of appropriate checks, an unprivileged guest userspace process can now force this transition and freely modify these fields. This allows the userspace to: 1. Disable interrupts, preventing other processes from running on the CPU. 2. Gain direct hardware I/O access by elevating EFLAGS.IOPL to 3. 3. Crash the guest kernel by setting CS and SS to resemble segments with RPL = 0 and triggering an exception. Since the kernel is unaware that the process entered virtual-8086 mode, it will misinterpret the exception as originating from kernel space. This patch fixes this bug by adding the missing CPL and long mode checks before jumping to the `return_to_vm86` label. Fixes: 90a9fdae1f1a ("more ring 0 operations") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3583 Signed-off-by: Andrey Polivoda Cc: qemu-devel@nongnu.org Cc: Paolo Bonzini Cc: Richard Henderson Link: https://lore.kernel.org/r/20260622082119.11903-1-apolivodaa433@gmail.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/seg_helper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index d5c174b7fd..6d424349a5 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -2077,7 +2077,8 @@ static inline void helper_ret_protected(CPUX86State *env, int shift, new_cs = popl(&sa) & 0xffff; if (is_iret) { new_eflags = popl(&sa); - if (new_eflags & VM_MASK) { + bool allow_vm86 = (cpl == 0) && !(env->hflags & HF_LMA_MASK); + if ((new_eflags & VM_MASK) && allow_vm86) { goto return_to_vm86; } } From 380b9596181760f371713604ea3472c56c19deb9 Mon Sep 17 00:00:00 2001 From: Andrey Polivoda Date: Sun, 21 Jun 2026 13:25:23 +1000 Subject: [PATCH 555/860] target/i386: decode opcode extensions group 3 /1 as TEST According to Table A-6 in Volume 3 of AMD64 Architecture Programmer's Manual, opcodes F6 and F7 (opcode extensions group 3) with ModRM's reg field values of 0 and 1 can be used to encode a TEST instruction. Although Intel 64 and IA-32 Architectures Software Developer's Manual leaves the cell 1 of opcode extensions group 3 blank in the opcode table (Table A-6, Volume 2D), the instruction in a group 3 with reg field of ModRM byte set to 1 actually behaves like TEST instruction on Intel CPUs. Currently, QEMU decodes group 3 instruction as TEST only if reg field of ModRM byte is 0. When the reg field is 1, QEMU raises a #UD exception. This behavior does not match real Intel and AMD hardware. This patch fixes this issue by duplicating the existing [0x00] and [0x08] X86_OP_ENTRYrr(AND, ...) entries into slots [0x01] and [0x09] in the opcodes_grp3 table. Fixes: d7c41a60d0c5 ("target/i386: move C0-FF opcodes to new decoder (except for x87)") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3580 Signed-off-by: Andrey Polivoda Cc: qemu-devel@nongnu.org Cc: Paolo Bonzini Cc: Richard Henderson Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20260621032524.1138213-1-apolivodaa433@gmail.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index faf74b00bb..9c429e113d 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -1624,6 +1624,7 @@ static void decode_group3(DisasContext *s, CPUX86State *env, X86OpEntry *entry, static const X86OpEntry opcodes_grp3[16] = { /* 0xf6 */ [0x00] = X86_OP_ENTRYrr(AND, E,b, I,b), + [0x01] = X86_OP_ENTRYrr(AND, E,b, I,b), [0x02] = X86_OP_ENTRY1(NOT, E,b, lock), [0x03] = X86_OP_ENTRY1(NEG, E,b, lock), [0x04] = X86_OP_ENTRYrr(MUL, E,b, 0,b, zextT0), @@ -1633,6 +1634,7 @@ static void decode_group3(DisasContext *s, CPUX86State *env, X86OpEntry *entry, /* 0xf7 */ [0x08] = X86_OP_ENTRYrr(AND, E,v, I,z), + [0x09] = X86_OP_ENTRYrr(AND, E,v, I,z), [0x0a] = X86_OP_ENTRY1(NOT, E,v, lock), [0x0b] = X86_OP_ENTRY1(NEG, E,v, lock), [0x0c] = X86_OP_ENTRYrr(MUL, E,v, 0,v, zextT0), From f3a89cded28054161d72feeb85a6fe082b4d1b70 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:23 +0200 Subject: [PATCH 556/860] target/i386/mshv: disable AMX TILE features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the time being we disable AMX TILE in partition processor features and CPUID b/c AMX TILE XSAVE state (XTILE_DATA) is 8KB, which exceeds the current fixed 4KB XSAVE buffer size. For now we filter it until buffer sizing is computed dynamically from CPUID. Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260710101534.664604-2-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 8 ++++++++ target/i386/mshv/mshv-cpu.c | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 72721d0f0d..5e8756ede2 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -203,6 +203,14 @@ static int create_partition(int mshv_fd, int *vm_fd) /* enable all */ disabled_xsave_features.as_uint64 = 0; + /* + * AMX TILE XSAVE state (XTILE_DATA) is 8KB, which exceeds the + * current fixed 4KB XSAVE buffer size. + */ + disabled_xsave_features.amx_tile_support = 1; + disabled_xsave_features.amx_bf16_support = 1; + disabled_xsave_features.amx_int8_support = 1; + disabled_xsave_features.amx_fp16_support = 1; /* * query host for supported processor features and disable unsupported diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 1c433c408c..8eca01a8fa 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -2156,6 +2156,22 @@ uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg) */ ret &= ~CPUID_7_0_ECX_LA57; } + if (func == 0x07 && idx == 0 && reg == R_EDX) { + /* + * AMX TILE XSAVE state (XTILE_DATA) is 8KB, which exceeds the + * current fixed 4KB XSAVE buffer size. Filter until buffer + * sizing is computed dynamically from CPUID. + */ + ret &= ~CPUID_7_0_EDX_AMX_TILE; + ret &= ~CPUID_7_0_EDX_AMX_BF16; + ret &= ~CPUID_7_0_EDX_AMX_INT8; + } + if (func == 0x07 && idx == 1 && reg == R_EAX) { + ret &= ~CPUID_7_1_EAX_AMX_FP16; + } + if (func == 0x07 && idx == 1 && reg == R_EDX) { + ret &= ~CPUID_7_1_EDX_AMX_COMPLEX; + } return ret; } From 3b599ae592db07e666cc2a7f1f449b4d792fbc95 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:33 +0200 Subject: [PATCH 557/860] target/i386/mshv: toggle fpu/xsave migration MSHV exposes overlapping legacy FP/SSE state through two paths: explicit Hyper-V FPU/XMM + registers and VP XSAVE state. There can be subtle inconsistencies across migrations when XSAVE is written after FPU state. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20260710101534.664604-12-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/mshv/mshv-cpu.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 8eca01a8fa..fa15e399cb 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -950,7 +950,7 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } - ret = get_fpu(cpu); + ret = get_xsave_state(cpu); if (ret < 0) { return ret; } @@ -960,7 +960,7 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } - ret = get_xsave_state(cpu); + ret = get_fpu(cpu); if (ret < 0) { return ret; } @@ -1506,7 +1506,7 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return ret; } - ret = set_fpu(cpu); + ret = set_xsave_state(cpu); if (ret < 0) { return ret; } @@ -1516,7 +1516,8 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return ret; } - ret = set_xsave_state(cpu); + /* INVARIANT: legacy FPU state must be restored after XSAVE */ + ret = set_fpu(cpu); if (ret < 0) { return ret; } From f27b4fcfd8f584e4509a18d2ea4019257e19d5b9 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:24 +0200 Subject: [PATCH 558/860] accel/mshv: introduce SaveVMHandler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This mechanism is used to handle more imperative partition-wide steps that have to be taken as part of a migration routine. Currently it's just a skeleton. Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260710101534.664604-3-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 5e8756ede2..b34fb34d05 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -39,6 +39,8 @@ #include "system/mshv.h" #include "system/mshv_int.h" #include "system/reset.h" +#include "migration/qemu-file-types.h" +#include "migration/register.h" #include "trace.h" #include #include @@ -544,6 +546,9 @@ static int mshv_init_vcpu(CPUState *cpu) return 0; } +static SaveVMHandlers savevm_mshv = { +}; + static int mshv_init(AccelState *as, MachineState *ms) { MshvState *s; @@ -599,6 +604,8 @@ static int mshv_init(AccelState *as, MachineState *ms) 0, "mshv-memory"); memory_listener_register(&mshv_io_listener, &address_space_io); + register_savevm_live("mshv", 0, 1, &savevm_mshv, s); + return 0; } From 922c1a4aac5a02f2782153ff5702652c4b332ca6 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:25 +0200 Subject: [PATCH 559/860] hw/i386/mshv: migrate REFERENCE_TIME MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a partition-wide state for which we use a dedicated hw clock facility, similar to KVM. We have to freeze the time for a partition before we are allowed to set it. We register a state change handler for the clock device and a post-load handler for migration state. In the post-load handler we toggle a flag that will set the reference time state on next state to "running" on the partition. We can move the time freeze and reference-time ioctls/hvcalls to the clock module. Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260710101534.664604-4-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + accel/mshv/mshv-all.c | 67 ++------------ accel/mshv/trace-events | 1 + hw/i386/meson.build | 1 + hw/i386/mshv/clock.c | 189 +++++++++++++++++++++++++++++++++++++++ hw/i386/mshv/meson.build | 4 + include/system/mshv.h | 3 + 7 files changed, 208 insertions(+), 58 deletions(-) create mode 100644 hw/i386/mshv/clock.c create mode 100644 hw/i386/mshv/meson.build diff --git a/MAINTAINERS b/MAINTAINERS index f249be744e..937c47f491 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -620,6 +620,7 @@ R: Wei Liu R: Doru Blânzeanu S: Supported F: target/i386/mshv/ +F: hw/i386/mshv/ X86 Instruction Emulator M: Roman Bolshakov diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index b34fb34d05..7b3c13cf1e 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -60,54 +60,6 @@ static int init_mshv(int *mshv_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 get_host_partition_property(int mshv_fd, uint32_t property_code, uint64_t *value) { @@ -330,9 +282,6 @@ static int create_vm(int mshv_fd, int *vm_fd) return -1; } - /* Always create a frozen partition */ - pause_vm(*vm_fd); - return 0; } @@ -578,13 +527,6 @@ static int mshv_init(AccelState *as, MachineState *ms) 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; @@ -606,6 +548,8 @@ static int mshv_init(AccelState *as, MachineState *ms) register_savevm_live("mshv", 0, 1, &savevm_mshv, s); + mshv_clock_init(); + return 0; } @@ -643,6 +587,13 @@ static int mshv_cpu_exec(CPUState *cpu) cpu->vcpu_dirty = false; } + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { + trace_mshv_interrupt_exit_request(cpu->cpu_index); + ret = EXCP_INTERRUPT; + break; + } + 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); diff --git a/accel/mshv/trace-events b/accel/mshv/trace-events index a4dffeb24a..859e8bfb0f 100644 --- a/accel/mshv/trace-events +++ b/accel/mshv/trace-events @@ -4,6 +4,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later mshv_start_vcpu_thread(const char* thread, uint32_t cpu) "thread=%s cpu_index=%d" +mshv_interrupt_exit_request(uint32_t cpu) "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" diff --git a/hw/i386/meson.build b/hw/i386/meson.build index b611fbb5a7..f4d3122863 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -39,6 +39,7 @@ i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', 'tdvf-hob.c')) subdir('kvm') subdir('xen') +subdir('mshv') i386_ss.add_all(xenpv_ss) diff --git a/hw/i386/mshv/clock.c b/hw/i386/mshv/clock.c new file mode 100644 index 0000000000..02c586503a --- /dev/null +++ b/hw/i386/mshv/clock.c @@ -0,0 +1,189 @@ +/* + * MSHV partition reference clock + * + * Copyright Microsoft, Corp. 2026 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "migration/vmstate.h" +#include "system/runstate.h" +#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 "system/mshv.h" +#include "system/mshv_int.h" + +/* + * Partition reference clock (HV_PARTITION_PROPERTY_REFERENCE_TIME), captured + * when the VM is stopped and re-applied when it resumes. + * + * Mirrors hw/i386/kvm/clock.c. + */ +typedef struct MshvClockState { + uint64_t ref_time; + bool ref_time_pending; +} MshvClockState; + +static MshvClockState mshv_clock; + +static int mshv_get_reference_time(int vm_fd, uint64_t *ref_time) +{ + struct hv_input_get_partition_property in = { 0 }; + struct hv_output_get_partition_property out = { 0 }; + struct mshv_root_hvcall args = { 0 }; + int ret; + + in.property_code = HV_PARTITION_PROPERTY_REFERENCE_TIME; + + args.code = HVCALL_GET_PARTITION_PROPERTY; + args.in_sz = sizeof(in); + args.in_ptr = (uint64_t)∈ + args.out_sz = sizeof(out); + args.out_ptr = (uint64_t)&out; + + ret = mshv_hvcall(vm_fd, &args); + if (ret < 0) { + error_report("Failed to get reference time"); + return -1; + } + + *ref_time = out.property_value; + return 0; +} + +static int mshv_set_reference_time(int vm_fd, uint64_t ref_time) +{ + struct hv_input_set_partition_property in = { 0 }; + struct mshv_root_hvcall args = { 0 }; + int ret; + + in.property_code = HV_PARTITION_PROPERTY_REFERENCE_TIME; + in.property_value = ref_time; + + 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 reference time"); + return -1; + } + + return 0; +} + +/* + * Freeze (freeze=1) or unfreeze (freeze=0) time for a partition. This will not + * pause/eject vCPU execution. It is assumed that the caller already has stopped + * the partition's vCPUs. + * + * NB: a partition's reference clock can only be written while the time is + * frozen. + */ +static int mshv_set_time_freeze(int vm_fd, int freeze) +{ + struct hv_input_set_partition_property in = { 0 }; + struct mshv_root_hvcall args = { 0 }; + int ret; + + in.property_code = HV_PARTITION_PROPERTY_TIME_FREEZE; + in.property_value = freeze; + + 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 void mshv_clock_vm_state_change(void *opaque, bool running, + RunState state) +{ + MshvClockState *s = opaque; + int vm_fd = mshv_state->vm; + int ret; + + if (running) { + /* Skip if we have nothing to restore, e.g. on initial boot. */ + if (!s->ref_time_pending) { + return; + } + + ret = mshv_set_time_freeze(vm_fd, 1); + if (ret < 0) { + error_report("Failed to freeze partition time on resume"); + abort(); + } + + /* INVARIANT: reference time can only be written if time is frozen. */ + ret = mshv_set_reference_time(vm_fd, s->ref_time); + if (ret < 0) { + error_report("Failed to restore reference time on resume"); + abort(); + } + + if (mshv_set_time_freeze(vm_fd, 0) < 0) { + error_report("Failed to unfreeze partition time on resume"); + abort(); + } + + s->ref_time_pending = false; + } else { + /* Skip if we already have a to-be-set ref time */ + if (s->ref_time_pending) { + return; + } + + ret = mshv_get_reference_time(vm_fd, &s->ref_time); + if (ret < 0) { + error_report("Failed to capture reference time on stop"); + abort(); + } + + s->ref_time_pending = true; + } +} + +/* + * The incoming reference time should be applied on the next resume before + * vCPUs start executing. + */ +static int mshv_clock_post_load(void *opaque, int version_id) +{ + MshvClockState *s = opaque; + + s->ref_time_pending = true; + + return 0; +} + +static const VMStateDescription vmstate_mshv_clock = { + .name = "mshv-clock", + .version_id = 1, + .minimum_version_id = 1, + .post_load = mshv_clock_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT64(ref_time, MshvClockState), + VMSTATE_END_OF_LIST() + }, +}; + +void mshv_clock_init(void) +{ + vmstate_register(NULL, 0, &vmstate_mshv_clock, &mshv_clock); + qemu_add_vm_change_state_handler(mshv_clock_vm_state_change, &mshv_clock); +} diff --git a/hw/i386/mshv/meson.build b/hw/i386/mshv/meson.build new file mode 100644 index 0000000000..0e556851b6 --- /dev/null +++ b/hw/i386/mshv/meson.build @@ -0,0 +1,4 @@ +i386_mshv_ss = ss.source_set() +i386_mshv_ss.add(files('clock.c')) + +i386_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss) diff --git a/include/system/mshv.h b/include/system/mshv.h index 46fe3fcebc..a6f815b822 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -55,6 +55,9 @@ DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, extern MshvState *mshv_state; +/* clock (partition reference time) */ +void mshv_clock_init(void); + /* interrupt */ int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector, uint32_t vp_index, bool logical_destination_mode, From 2f6c2ede4688f90a3583cfb2050de66a100fc5cb Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:26 +0200 Subject: [PATCH 560/860] accel/mshv: install dummy handler for SIG_IPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is similar to HVF's implementation. We want to interrupt the blocking vcpu run. The self-kick was effectively a no-op for mshv. Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260710101534.664604-5-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 7b3c13cf1e..f9511ca050 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -621,17 +621,13 @@ static int mshv_cpu_exec(CPUState *cpu) } /* - * 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. + * We need a dummy handler to make SIG_IPI a deliverable signal. The kernel + * handler will be woken up by the caught signal and instruct the hypervisor + * to suspend execution (the concrete mechanism differs between schedulers) + * and return to userspace. */ -static void sa_ipi_handler(int sig) +static void dummy_handler(int sig) { - /* TODO: call IOCTL to set_immediate_exit, once implemented. */ - - qemu_cpu_kick_self(); } static void init_signal(CPUState *cpu) @@ -641,7 +637,7 @@ static void init_signal(CPUState *cpu) sigset_t set; memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = sa_ipi_handler; + sigact.sa_handler = dummy_handler; sigaction(SIG_IPI, &sigact, NULL); pthread_sigmask(SIG_BLOCK, NULL, &set); From 662b6c053ec24517b4cbfdf1911e2e78cf73aee4 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:27 +0200 Subject: [PATCH 561/860] target/i386/mshv: migrate LAPIC state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change implements loading and storing the hyperv lapic state as part of the load/store routines for a vcpu. The HyperV LAPIC is similar to the the split-irqchip in KVM. MSHV currently keeps PIC/IOAPIC emulation in userspace, while LAPIC interrupt injection is handled through hypercalls. We introduced dedicated apic infra in hw/i386/mshv to handle the migration and move lapic related functions from target/i386/mshv there. References have been the WHPX's whpx-apic implemenation and the mshv-ioctls crate's get_/set_lapic() impl for the mapping between MSHV/QEMU lapic state. We are mapping the lapic state that we receive from the hypervisor to fields in APICCommonState. Common fields are used where feasible, with an mshv-specific MshvAPICState object that carries mshv-specific fields. We have introduced a guard in pic_irq_request() that will early exit for the mshv accelerator, because mshv cannot take part in the userland path for legacy PIC interrupt injection. The TSC_DEADLINE MSR is also migrated as part of LAPIC migration. Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260710101534.664604-6-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 1 + hw/i386/meson.build | 2 +- hw/i386/mshv/apic.c | 395 +++++++++++++++++++++++++++++++++ hw/i386/mshv/meson.build | 1 + hw/i386/x86-cpu.c | 6 + include/hw/hyperv/hvgdk_mini.h | 2 + include/hw/i386/apic-msidef.h | 1 + include/system/mshv.h | 2 + include/system/mshv_int.h | 8 +- target/i386/cpu-apic.c | 3 + target/i386/mshv/mshv-cpu.c | 151 ++++--------- target/i386/mshv/msr.c | 2 + 12 files changed, 460 insertions(+), 114 deletions(-) create mode 100644 hw/i386/mshv/apic.c diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index f9511ca050..1516475f34 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -813,6 +813,7 @@ static void mshv_accel_ops_class_init(ObjectClass *oc, const void *data) ops->synchronize_state = mshv_cpu_synchronize; ops->synchronize_pre_loadvm = mshv_cpu_synchronize_pre_loadvm; ops->cpus_are_resettable = mshv_cpus_are_resettable; + ops->cpu_thread_is_idle = mshv_vcpu_thread_is_idle; ops->handle_interrupt = generic_handle_interrupt; } diff --git a/hw/i386/meson.build b/hw/i386/meson.build index f4d3122863..39ac8c9edc 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -38,8 +38,8 @@ i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'), i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', 'tdvf-hob.c')) subdir('kvm') -subdir('xen') subdir('mshv') +subdir('xen') i386_ss.add_all(xenpv_ss) diff --git a/hw/i386/mshv/apic.c b/hw/i386/mshv/apic.c new file mode 100644 index 0000000000..9188a93b2f --- /dev/null +++ b/hw/i386/mshv/apic.c @@ -0,0 +1,395 @@ +/* + * MSHV in-kernel APIC support + * + * Copyright Microsoft, Corp. 2026 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/memalign.h" +#include "qemu/error-report.h" +#include "hw/i386/apic_internal.h" +#include "hw/i386/apic-msidef.h" +#include "hw/pci/msi.h" +#include "migration/vmstate.h" +#include "qemu/typedefs.h" +#include "system/hw_accel.h" +#include "system/mshv.h" +#include "system/mshv_int.h" + +typedef struct hv_local_interrupt_controller_state + hv_local_interrupt_controller_state; + +#define TYPE_MSHV_APIC "mshv-apic" +OBJECT_DECLARE_SIMPLE_TYPE(MshvAPICState, MSHV_APIC) + +struct MshvAPICState { + APICCommonState parent_obj; + + uint32_t apic_version; + uint32_t apic_lvt_cmci; + uint32_t apic_error_status; + uint32_t apic_counter_value; + uint32_t apic_remote_read; +}; + +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 = mshv_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 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 = mshv_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 void populate_apic_state(CPUState *cpu, + const hv_local_interrupt_controller_state *hv) +{ + X86CPU *x86cpu = X86_CPU(cpu); + MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state); + APICCommonState *s = &ms->parent_obj; + size_t i; + + /* + * x2APIC: + * - APIC ID is the full 32-bit initial_apic_id + * - LDR is read-only, architecturally derived from the ID + * - DFR does not exist in x2APIC mode + */ + if (is_x2apic_mode(s)) { + s->initial_apic_id = hv->apic_id; + } else { + s->id = hv->apic_id >> 24; + s->log_dest = hv->apic_ldr >> 24; + s->dest_mode = hv->apic_dfr >> 28; + } + ms->apic_version = hv->apic_version; + s->spurious_vec = hv->apic_spurious; + for (i = 0; i < 8; i++) { + s->isr[i] = hv->apic_isr[i]; + s->tmr[i] = hv->apic_tmr[i]; + s->irr[i] = hv->apic_irr[i]; + } + s->esr = hv->apic_esr; + s->icr[1] = hv->apic_icr_high; + s->icr[0] = hv->apic_icr_low; + + s->lvt[APIC_LVT_TIMER] = hv->apic_lvt_timer; + s->lvt[APIC_LVT_THERMAL] = hv->apic_lvt_thermal; + s->lvt[APIC_LVT_PERFORM] = hv->apic_lvt_perfmon; + s->lvt[APIC_LVT_LINT0] = hv->apic_lvt_lint0; + s->lvt[APIC_LVT_LINT1] = hv->apic_lvt_lint1; + s->lvt[APIC_LVT_ERROR] = hv->apic_lvt_error; + ms->apic_lvt_cmci = hv->apic_lvt_cmci; + + ms->apic_error_status = hv->apic_error_status; + s->initial_count = hv->apic_initial_count; + ms->apic_counter_value = hv->apic_counter_value; + s->divide_conf = hv->apic_divide_configuration; + ms->apic_remote_read = hv->apic_remote_read; +} + +static uint32_t set_apic_delivery_mode(uint32_t reg, uint32_t mode) +{ + return ((reg) & ~0x700) | ((mode) << 8); +} + +int mshv_init_lint(CPUState *cpu) +{ + uint32_t *lvt_lint0, *lvt_lint1; + int cpu_fd = mshv_vcpufd(cpu); + int ret; + 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? */ + + ret = set_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return -1; + } + + populate_apic_state(cpu, &lapic_state); + + return 0; +} + +static void populate_hv_lapic_state(hv_local_interrupt_controller_state *hv, + const CPUState *cpu) +{ + uint32_t x2apic_id; + X86CPU *x86cpu = X86_CPU(cpu); + MshvAPICState *ms = MSHV_APIC(x86cpu->apic_state); + APICCommonState *s = &ms->parent_obj; + size_t i; + + /* + * x2APIC: + * - APIC ID is the full 32-bit initial_apic_id + * - LDR is read-only, architecturally derived from the ID + * - DFR does not exist in x2APIC mode + */ + if (is_x2apic_mode(s)) { + x2apic_id = s->initial_apic_id; + + hv->apic_id = x2apic_id; + hv->apic_ldr = ((x2apic_id >> 4) << 16) | (1 << (x2apic_id & 0xf)); + hv->apic_dfr = 0; + } else { + hv->apic_id = s->id << 24; + hv->apic_ldr = s->log_dest << 24; + hv->apic_dfr = s->dest_mode << 28 | 0x0fffffff; + } + hv->apic_version = ms->apic_version; + hv->apic_spurious = s->spurious_vec; + for (i = 0; i < 8; i++) { + hv->apic_isr[i] = s->isr[i]; + hv->apic_tmr[i] = s->tmr[i]; + hv->apic_irr[i] = s->irr[i]; + } + hv->apic_esr = s->esr; + hv->apic_icr_high = s->icr[1]; + hv->apic_icr_low = s->icr[0]; + + hv->apic_lvt_timer = s->lvt[APIC_LVT_TIMER]; + hv->apic_lvt_thermal = s->lvt[APIC_LVT_THERMAL]; + hv->apic_lvt_perfmon = s->lvt[APIC_LVT_PERFORM]; + hv->apic_lvt_lint0 = s->lvt[APIC_LVT_LINT0]; + hv->apic_lvt_lint1 = s->lvt[APIC_LVT_LINT1]; + hv->apic_lvt_error = s->lvt[APIC_LVT_ERROR]; + hv->apic_lvt_cmci = ms->apic_lvt_cmci; + + hv->apic_error_status = ms->apic_error_status; + hv->apic_initial_count = s->initial_count; + hv->apic_counter_value = ms->apic_counter_value; + hv->apic_divide_configuration = s->divide_conf; + hv->apic_remote_read = ms->apic_remote_read; +} + +int mshv_set_lapic(const CPUState *cpu) +{ + int cpu_fd = mshv_vcpufd(cpu); + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + + populate_hv_lapic_state(&lapic_state, cpu); + + return set_lapic(cpu_fd, &lapic_state); +} + +int mshv_get_lapic(CPUState *cpu) +{ + int cpu_fd = mshv_vcpufd(cpu); + int ret; + struct hv_local_interrupt_controller_state lapic_state = { 0 }; + + ret = get_lapic(cpu_fd, &lapic_state); + if (ret < 0) { + return -1; + } + + populate_apic_state(cpu, &lapic_state); + + return 0; +} + +static int mshv_apic_set_base(APICCommonState *s, uint64_t val) +{ + s->apicbase = val; + + return 0; +} + +static void mshv_apic_set_tpr(APICCommonState *s, uint8_t val) +{ + s->tpr = (val & APIC_PR_SUB_CLASS) << APIC_PR_CLASS_SHIFT; +} + +static uint8_t mshv_apic_get_tpr(APICCommonState *s) +{ + return s->tpr >> APIC_PR_CLASS_SHIFT; +} + +static void mshv_apic_external_nmi(APICCommonState *s) +{ +} + +static void mshv_apic_vapic_base_update(APICCommonState *s) +{ +} + +static void mshv_send_msi(MSIMessage *msi) +{ + uint64_t addr; + uint32_t data, dest; + uint8_t vector, dest_mode, trigger_mode, delivery; + + addr = msi->address; + data = msi->data; + dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT | + (addr >> 32); + vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; + dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; + trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; + delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & + MSI_DATA_DELIVERY_MODE_MASK; + + mshv_request_interrupt(mshv_state, delivery, vector, dest, dest_mode, + trigger_mode); +} + +static uint64_t mshv_apic_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + return UINT64_MAX; +} + +static void mshv_apic_mem_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + MSIMessage msg = { .address = addr, .data = data }; + + mshv_send_msi(&msg); +} + +static const MemoryRegionOps mshv_apic_io_ops = { + .read = mshv_apic_mem_read, + .write = mshv_apic_mem_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void mshv_apic_reset(APICCommonState *s) +{ + s->wait_for_sipi = 0; +} + +static const VMStateDescription vmstate_mshv_apic = { + .name = "mshv-apic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(apic_version, MshvAPICState), + VMSTATE_UINT32(apic_lvt_cmci, MshvAPICState), + VMSTATE_UINT32(apic_error_status, MshvAPICState), + VMSTATE_UINT32(apic_counter_value, MshvAPICState), + VMSTATE_UINT32(apic_remote_read, MshvAPICState), + VMSTATE_END_OF_LIST() + } +}; + +static void mshv_apic_realize(DeviceState *dev, Error **errp) +{ + APICCommonState *s = APIC_COMMON(dev); + MshvAPICState *ms = MSHV_APIC(dev); + + memory_region_init_io(&s->io_memory, OBJECT(s), &mshv_apic_io_ops, s, + "mshv-apic-msi", APIC_SPACE_SIZE); + + msi_nonbroken = true; + + /* + * We register this state explicity, rather than going via dc->vmsd. + * The auto-wiring would register the state with + * instance_id == VMSTATE_INSTANCE_ID_ANY, which for the APIC doesn't + * work, b/c the ID carries semantic meaning for restoring the state + * on the destination (which vcpu it belongs to). + */ + vmstate_register_with_alias_id(NULL, + s->initial_apic_id, &vmstate_mshv_apic, ms, + -1, 0, NULL); +} + +static void mshv_apic_unrealize(DeviceState *dev) +{ + MshvAPICState *ms = MSHV_APIC(dev); + + vmstate_unregister(NULL, &vmstate_mshv_apic, ms); +} + +static void mshv_apic_class_init(ObjectClass *klass, const void *data) +{ + APICCommonClass *k = APIC_COMMON_CLASS(klass); + + k->realize = mshv_apic_realize; + k->unrealize = mshv_apic_unrealize; + k->reset = mshv_apic_reset; + k->set_base = mshv_apic_set_base; + k->set_tpr = mshv_apic_set_tpr; + k->get_tpr = mshv_apic_get_tpr; + k->external_nmi = mshv_apic_external_nmi; + k->vapic_base_update = mshv_apic_vapic_base_update; + k->send_msi = mshv_send_msi; +} + +static const TypeInfo mshv_apic_info = { + .name = TYPE_MSHV_APIC, + .parent = TYPE_APIC_COMMON, + .instance_size = sizeof(MshvAPICState), + .class_init = mshv_apic_class_init, +}; + +static void mshv_apic_register_types(void) +{ + type_register_static(&mshv_apic_info); +} + +type_init(mshv_apic_register_types) diff --git a/hw/i386/mshv/meson.build b/hw/i386/mshv/meson.build index 0e556851b6..c631ee1302 100644 --- a/hw/i386/mshv/meson.build +++ b/hw/i386/mshv/meson.build @@ -1,4 +1,5 @@ i386_mshv_ss = ss.source_set() i386_mshv_ss.add(files('clock.c')) +i386_mshv_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c')) i386_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss) diff --git a/hw/i386/x86-cpu.c b/hw/i386/x86-cpu.c index 95e08e3c2a..fba313376c 100644 --- a/hw/i386/x86-cpu.c +++ b/hw/i386/x86-cpu.c @@ -22,6 +22,7 @@ */ #include "qemu/osdep.h" #include "system/whpx.h" +#include "system/mshv.h" #include "system/cpu-timers.h" #include "trace.h" @@ -44,6 +45,11 @@ static void pic_irq_request(void *opaque, int irq, int level) X86CPU *cpu = X86_CPU(cs); trace_x86_pic_interrupt(irq, level); + + if (mshv_irqchip_in_kernel()) { + return; + } + if (cpu_is_apic_enabled(cpu->apic_state) && !kvm_irqchip_in_kernel() && !whpx_irqchip_in_kernel()) { CPU_FOREACH(cs) { diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h index f8838a31bb..0602a7c6cc 100644 --- a/include/hw/hyperv/hvgdk_mini.h +++ b/include/hw/hyperv/hvgdk_mini.h @@ -168,6 +168,7 @@ typedef enum hv_register_name { /* Available */ HV_X64_REGISTER_SPEC_CTRL = 0x00080084, + HV_X64_REGISTER_TSC_DEADLINE = 0x00080095, HV_X64_REGISTER_TSC_ADJUST = 0x00080096, /* CET / Shadow Stack */ @@ -930,6 +931,7 @@ struct hv_cpuid { #define IA32_MSR_DEBUG_CTL 0x1D9 #define IA32_MSR_SPEC_CTRL 0x00000048 #define IA32_MSR_TSC_ADJUST 0x0000003b +#define IA32_MSR_TSC_DEADLINE 0x000006e0 #define IA32_MSR_MISC_ENABLE 0x000001a0 diff --git a/include/hw/i386/apic-msidef.h b/include/hw/i386/apic-msidef.h index 420b41167d..6b860b5807 100644 --- a/include/hw/i386/apic-msidef.h +++ b/include/hw/i386/apic-msidef.h @@ -13,6 +13,7 @@ #define MSI_DATA_VECTOR_MASK 0x000000ff #define MSI_DATA_DELIVERY_MODE_SHIFT 8 +#define MSI_DATA_DELIVERY_MODE_MASK 7 #define MSI_DATA_LEVEL_SHIFT 14 #define MSI_DATA_TRIGGER_SHIFT 15 diff --git a/include/system/mshv.h b/include/system/mshv.h index a6f815b822..f98ddff9b5 100644 --- a/include/system/mshv.h +++ b/include/system/mshv.h @@ -41,9 +41,11 @@ extern bool mshv_allowed; #define mshv_enabled() (mshv_allowed) #define mshv_msi_via_irqfd_enabled() mshv_enabled() +#define mshv_irqchip_in_kernel() mshv_enabled() #else /* CONFIG_MSHV_IS_POSSIBLE */ #define mshv_enabled() false #define mshv_msi_via_irqfd_enabled() mshv_enabled() +#define mshv_irqchip_in_kernel() mshv_enabled() #endif #define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv") diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index b91c4d661a..c2f13c0194 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -105,10 +105,16 @@ void mshv_arch_amend_proc_features( void mshv_arch_disable_partition_proc_features( union hv_partition_processor_features *disabled_features); int mshv_arch_post_init_vm(int vm_fd); - +int mshv_get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state); +int mshv_set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state); typedef struct mshv_root_hvcall mshv_root_hvcall; int mshv_hvcall(int fd, const mshv_root_hvcall *args); +/* apic */ +int mshv_init_lint(CPUState *cpu); +int mshv_set_lapic(const CPUState *cpu); +int mshv_get_lapic(CPUState *cpu); + /* memory */ typedef struct MshvMemoryRegion { uint64_t guest_phys_addr; diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c index 04b7257ad1..b4cf048a7c 100644 --- a/target/i386/cpu-apic.c +++ b/target/i386/cpu-apic.c @@ -14,6 +14,7 @@ #include "system/hw_accel.h" #include "system/kvm.h" #include "system/xen.h" +#include "system/mshv.h" #include "system/address-spaces.h" #include "hw/core/qdev-properties.h" #include "hw/i386/apic_internal.h" @@ -34,6 +35,8 @@ APICCommonClass *apic_get_class(Error **errp) apic_type = "xen-apic"; } else if (whpx_irqchip_in_kernel()) { apic_type = "whpx-apic"; + } else if (mshv_enabled()) { + apic_type = "mshv-apic"; } return APIC_COMMON_CLASS(object_class_by_name(apic_type)); diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index fa15e399cb..00e948903d 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -21,7 +21,6 @@ #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 "host-cpu.h" @@ -955,6 +954,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } + ret = mshv_get_lapic(cpu); + if (ret < 0) { + return ret; + } + ret = mshv_get_msrs(cpu); if (ret < 0) { return ret; @@ -1377,116 +1381,6 @@ static int set_xc_reg(const CPUState *cpu) 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(const CPUState *cpu, - 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 }; - int cpu_fd = mshv_vcpufd(cpu); - - 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(const CPUState *cpu, - 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 }; - int cpu_fd = mshv_vcpufd(cpu); - - 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 init_lint(const CPUState *cpu) -{ - int ret; - uint32_t *lvt_lint0, *lvt_lint1; - - struct hv_local_interrupt_controller_state lapic_state = { 0 }; - ret = get_lapic(cpu, &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, &lapic_state); -} - int mshv_arch_store_vcpu_state(const CPUState *cpu) { int ret; @@ -1511,6 +1405,12 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return ret; } + /* INVARIANT: special regs (APIC_BASE) must be restored before LAPIC */ + ret = mshv_set_lapic(cpu); + if (ret < 0) { + return ret; + } + ret = mshv_set_msrs(cpu); if (ret < 0) { return ret; @@ -2100,7 +2000,7 @@ void mshv_arch_init_vcpu(CPUState *cpu) ret = mshv_init_msrs(cpu); assert(ret == 0); - ret = init_lint(cpu); + ret = mshv_init_lint(cpu); assert(ret == 0); } @@ -2247,6 +2147,33 @@ static void mshv_cpu_xsave_init(void) } } +int mshv_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; +} + + +int mshv_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 void mshv_cpu_instance_init(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c index 8c220a9942..a5f639c3ca 100644 --- a/target/i386/mshv/msr.c +++ b/target/i386/mshv/msr.c @@ -60,6 +60,8 @@ static const MshvMsrEnvMap msr_env_map[] = { offsetof(CPUX86State, tsc_aux) }, { IA32_MSR_TSC_ADJUST, HV_X64_REGISTER_TSC_ADJUST, offsetof(CPUX86State, tsc_adjust) }, + { IA32_MSR_TSC_DEADLINE, HV_X64_REGISTER_TSC_DEADLINE, + offsetof(CPUX86State, tsc_deadline) }, /* Hyper-V per-partition MSRs */ { HV_X64_MSR_HYPERCALL, HV_X64_REGISTER_HYPERCALL, From 65867d2edea0ec2cb1e7de338a998e0d7d5ee1aa Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:28 +0200 Subject: [PATCH 562/860] target/i386/mshv: migrate Synic SINT MSRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate HyperV SynIC SINT MSRs. We can only read/write those if SCONTROL is enabled in the guest, hence we have to split the SINT MSR out and make reading/writing them dependent on that MSR. Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260710101534.664604-7-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/mshv/msr.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c index a5f639c3ca..5288e32b6d 100644 --- a/target/i386/mshv/msr.c +++ b/target/i386/mshv/msr.c @@ -331,6 +331,8 @@ int mshv_get_msrs(CPUState *cpu) struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT]; size_t i, j; uint32_t name; + X86CPU *x86cpu = X86_CPU(cpu); + bool synic_enabled; set_hv_name_in_assocs(assocs, n_assocs); @@ -357,6 +359,27 @@ int mshv_get_msrs(CPUState *cpu) store_in_env(cpu, assocs, n_assocs); + /* Read SINT MSRs only if SynIC is enabled */ + synic_enabled = x86cpu->env.msr_hv_synic_control & 1; + if (synic_enabled) { + QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT); + + for (i = 0; i < HV_SINT_COUNT; i++) { + assocs[i].name = HV_REGISTER_SINT0 + i; + } + + ret = mshv_get_generic_regs(cpu, assocs, HV_SINT_COUNT); + if (ret < 0) { + error_report("Failed to get SynIC SINT MSRs"); + return -errno; + } + + for (i = 0; i < HV_SINT_COUNT; i++) { + uint64_t hv_sint_value = assocs[i].value.reg64; + x86cpu->env.msr_hv_synic_sint[i] = hv_sint_value; + } + } + return 0; } @@ -391,6 +414,8 @@ int mshv_set_msrs(const CPUState *cpu) struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT]; int ret; size_t i, j; + X86CPU *x86cpu = X86_CPU(cpu); + bool synic_enabled = x86cpu->env.msr_hv_synic_control & 1; load_from_env(cpu, assocs, n_assocs); @@ -423,5 +448,21 @@ int mshv_set_msrs(const CPUState *cpu) return -errno; } + /* SINT MSRs can only be written if SCONTROL has been set, so we split */ + if (synic_enabled) { + QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT); + + for (i = 0; i < HV_SINT_COUNT; i++) { + assocs[i].name = HV_REGISTER_SINT0 + i; + assocs[i].value.reg64 = x86cpu->env.msr_hv_synic_sint[i]; + } + + ret = mshv_set_generic_regs(cpu, assocs, HV_SINT_COUNT); + if (ret < 0) { + error_report("Failed to set SynIC SINT MSRs"); + return -errno; + } + } + return 0; } From e514ff235b41c5582becc821b79f466d62f90c83 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:29 +0200 Subject: [PATCH 563/860] target/i386/mshv: migrate SIMP and SIEFP state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This part SynIC state is retrieved from the hypervisor via aligned state pages: - Add new synic source file - Centralize the synic_enabled() check - r/w pages from the hyper via aligned pages - only handle pages when synic is enabled - add buffers for migration to VM state Signed-off-by: Magnus Kulke Reviewed-by: Doru Blânzeanu Link: https://lore.kernel.org/r/20260710101534.664604-8-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- include/system/mshv_int.h | 7 ++ target/i386/cpu.h | 5 ++ target/i386/machine.c | 26 ++++++ target/i386/mshv/meson.build | 1 + target/i386/mshv/mshv-cpu.c | 64 +++++++++++++++ target/i386/mshv/msr.c | 7 +- target/i386/mshv/synic.c | 155 +++++++++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 5 deletions(-) create mode 100644 target/i386/mshv/synic.c diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index c2f13c0194..bc023d3535 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -138,4 +138,11 @@ int mshv_init_msrs(const CPUState *cpu); int mshv_get_msrs(CPUState *cpu); int mshv_set_msrs(const CPUState *cpu); +/* synic */ +int mshv_get_simp(int cpu_fd, uint8_t *page); +int mshv_set_simp(int cpu_fd, const uint8_t *page); +int mshv_get_siefp(int cpu_fd, uint8_t *page); +int mshv_set_siefp(int cpu_fd, const uint8_t *page); +bool mshv_synic_enabled(const CPUState *cpu); + #endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index fff701c6c4..fcde895600 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -33,6 +33,7 @@ #include "qemu/cpu-float.h" #include "qemu/timer.h" #include "standard-headers/asm-x86/kvm_para.h" +#include "hw/hyperv/hvgdk_mini.h" #define XEN_NR_VIRQS 24 @@ -2301,6 +2302,10 @@ typedef struct CPUArchState { #if defined(CONFIG_HVF) || defined(CONFIG_MSHV) || defined(CONFIG_WHPX) void *emu_mmio_buf; #endif +#if defined(CONFIG_MSHV) + uint8_t hv_simp_page[HV_HYP_PAGE_SIZE]; + uint8_t hv_siefp_page[HV_HYP_PAGE_SIZE]; +#endif uint64_t mcg_cap; uint64_t mcg_ctl; diff --git a/target/i386/machine.c b/target/i386/machine.c index df0e0c178e..023a397ab8 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -951,6 +951,29 @@ static const VMStateDescription vmstate_msr_hyperv_reenlightenment = { } }; +#ifdef CONFIG_MSHV +static bool mshv_synic_vp_state_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + /* Only migrate SIMP/SIEFP if SynIC is enabled */ + return env->msr_hv_synic_control & 1; +} + +static const VMStateDescription vmstate_mshv_synic_vp_state = { + .name = "cpu/mshv_synic_vp_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = mshv_synic_vp_state_needed, + .fields = (const VMStateField[]) { + VMSTATE_BUFFER(env.hv_simp_page, X86CPU), + VMSTATE_BUFFER(env.hv_siefp_page, X86CPU), + VMSTATE_END_OF_LIST() + } +}; +#endif + static bool avx512_needed(void *opaque) { X86CPU *cpu = opaque; @@ -1915,6 +1938,9 @@ const VMStateDescription vmstate_x86_cpu = { &vmstate_cet, #ifdef TARGET_X86_64 &vmstate_apx, +#endif +#ifdef CONFIG_MSHV + &vmstate_mshv_synic_vp_state, #endif NULL } diff --git a/target/i386/mshv/meson.build b/target/i386/mshv/meson.build index 6091c21887..31ff4cc995 100644 --- a/target/i386/mshv/meson.build +++ b/target/i386/mshv/meson.build @@ -3,6 +3,7 @@ i386_mshv_ss = ss.source_set() i386_mshv_ss.add(files( 'mshv-cpu.c', 'msr.c', + 'synic.c', )) i386_system_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss) diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 00e948903d..ea6a660d05 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -111,6 +111,33 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = { static int set_special_regs(const CPUState *cpu); +static int get_synic_state(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + int cpu_fd = mshv_vcpufd(cpu); + int ret; + + /* SIMP/SIEFP can only be read when SynIC is enabled */ + if (!mshv_synic_enabled(cpu)) { + return 0; + } + + ret = mshv_get_simp(cpu_fd, env->hv_simp_page); + if (ret < 0) { + error_report("failed to get simp state"); + return -1; + } + + ret = mshv_get_siefp(cpu_fd, env->hv_siefp_page); + if (ret < 0) { + error_report("failed to get siefp state"); + return -1; + } + + return 0; +} + static int get_xsave_state(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); @@ -969,6 +996,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } + ret = get_synic_state(cpu); + if (ret < 0) { + return ret; + } + ret = get_vcpu_events(cpu); if (ret < 0) { return ret; @@ -1381,6 +1413,33 @@ static int set_xc_reg(const CPUState *cpu) return 0; } +static int set_synic_state(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + int cpu_fd = mshv_vcpufd(cpu); + int ret; + + /* SIMP/SIEFP can only be written when SynIC is enabled */ + if (!mshv_synic_enabled(cpu)) { + return 0; + } + + ret = mshv_set_simp(cpu_fd, env->hv_simp_page); + if (ret < 0) { + error_report("failed to set simp state"); + return -1; + } + + ret = mshv_set_siefp(cpu_fd, env->hv_siefp_page); + if (ret < 0) { + error_report("failed to set siefp state"); + return -1; + } + + return 0; +} + int mshv_arch_store_vcpu_state(const CPUState *cpu) { int ret; @@ -1422,6 +1481,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return ret; } + ret = set_synic_state(cpu); + if (ret < 0) { + return ret; + } + ret = set_vcpu_events(cpu); if (ret < 0) { return ret; diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c index 5288e32b6d..d3788d7715 100644 --- a/target/i386/mshv/msr.c +++ b/target/i386/mshv/msr.c @@ -332,7 +332,6 @@ int mshv_get_msrs(CPUState *cpu) size_t i, j; uint32_t name; X86CPU *x86cpu = X86_CPU(cpu); - bool synic_enabled; set_hv_name_in_assocs(assocs, n_assocs); @@ -360,8 +359,7 @@ int mshv_get_msrs(CPUState *cpu) store_in_env(cpu, assocs, n_assocs); /* Read SINT MSRs only if SynIC is enabled */ - synic_enabled = x86cpu->env.msr_hv_synic_control & 1; - if (synic_enabled) { + if (mshv_synic_enabled(cpu)) { QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT); for (i = 0; i < HV_SINT_COUNT; i++) { @@ -415,7 +413,6 @@ int mshv_set_msrs(const CPUState *cpu) int ret; size_t i, j; X86CPU *x86cpu = X86_CPU(cpu); - bool synic_enabled = x86cpu->env.msr_hv_synic_control & 1; load_from_env(cpu, assocs, n_assocs); @@ -449,7 +446,7 @@ int mshv_set_msrs(const CPUState *cpu) } /* SINT MSRs can only be written if SCONTROL has been set, so we split */ - if (synic_enabled) { + if (mshv_synic_enabled(cpu)) { QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT); for (i = 0; i < HV_SINT_COUNT; i++) { diff --git a/target/i386/mshv/synic.c b/target/i386/mshv/synic.c new file mode 100644 index 0000000000..8f9fee6ed7 --- /dev/null +++ b/target/i386/mshv/synic.c @@ -0,0 +1,155 @@ +/* + * QEMU MSHV SynIC support + * + * Copyright Microsoft, Corp. 2026 + * + * Authors: Magnus Kulke + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/memalign.h" +#include "qemu/error-report.h" + +#include "system/mshv.h" +#include "system/mshv_int.h" + +#include "linux/mshv.h" +#include "hw/hyperv/hvgdk_mini.h" +#include "cpu.h" + +#include + +bool mshv_synic_enabled(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + + return x86cpu->env.msr_hv_synic_control & 1; +} + +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 vp state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +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 vp state: %s", strerror(errno)); + return -1; + } + + return 0; +} + +int mshv_get_simp(int cpu_fd, uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIMP; + + ret = get_vp_state(cpu_fd, &args); + + if (ret < 0) { + qemu_vfree(buffer); + error_report("failed to get simp"); + return -1; + } + + memcpy(page, buffer, HV_HYP_PAGE_SIZE); + qemu_vfree(buffer); + + return 0; +} + +int mshv_set_simp(int cpu_fd, const uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIMP; + + assert(page); + memcpy(buffer, page, HV_HYP_PAGE_SIZE); + + ret = set_vp_state(cpu_fd, &args); + qemu_vfree(buffer); + + if (ret < 0) { + error_report("failed to set simp"); + return -1; + } + + return 0; +} + +int mshv_get_siefp(int cpu_fd, uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIEFP, + + ret = get_vp_state(cpu_fd, &args); + + if (ret < 0) { + qemu_vfree(buffer); + error_report("failed to get siefp"); + return -1; + } + + memcpy(page, buffer, HV_HYP_PAGE_SIZE); + qemu_vfree(buffer); + + return 0; +} + +int mshv_set_siefp(int cpu_fd, const uint8_t *page) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SIEFP, + + assert(page); + memcpy(buffer, page, HV_HYP_PAGE_SIZE); + + ret = set_vp_state(cpu_fd, &args); + qemu_vfree(buffer); + + if (ret < 0) { + error_report("failed to set simp"); + return -1; + } + + return 0; +} From f2c8f7bcd424e17aea74fdb84d739887f1d52aa1 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:30 +0200 Subject: [PATCH 564/860] target/i386/mshv: migrate STIMER state This part of Synic state is retrieved via a mem-aligned page. We declare the required space (size reference: rust-vmm/mshv) as a buffer on the VM state struct for inclusion in a migration. Other than other SynIC features, STIMER doesn't depend on SCONTROL being set. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20260710101534.664604-9-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- include/system/mshv_int.h | 2 ++ target/i386/cpu.h | 5 ++++ target/i386/machine.c | 20 +++++++++++++++ target/i386/mshv/mshv-cpu.c | 12 +++++++++ target/i386/mshv/synic.c | 51 +++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index bc023d3535..063852115e 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -144,5 +144,7 @@ int mshv_set_simp(int cpu_fd, const uint8_t *page); int mshv_get_siefp(int cpu_fd, uint8_t *page); int mshv_set_siefp(int cpu_fd, const uint8_t *page); bool mshv_synic_enabled(const CPUState *cpu); +int mshv_get_synthetic_timers(int cpu_fd, uint8_t *state); +int mshv_set_synthetic_timers(int cpu_fd, const uint8_t *state); #endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index fcde895600..641f3ee5c2 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -45,6 +45,10 @@ #define ELF_MACHINE_UNAME "i686" #endif +#ifdef CONFIG_MSHV +#define MSHV_STIMERS_STATE_SIZE 200 +#endif + enum { R_EAX = 0, R_ECX = 1, @@ -2305,6 +2309,7 @@ typedef struct CPUArchState { #if defined(CONFIG_MSHV) uint8_t hv_simp_page[HV_HYP_PAGE_SIZE]; uint8_t hv_siefp_page[HV_HYP_PAGE_SIZE]; + uint8_t hv_synthetic_timers_state[MSHV_STIMERS_STATE_SIZE]; #endif uint64_t mcg_cap; diff --git a/target/i386/machine.c b/target/i386/machine.c index 023a397ab8..8d69d7e25e 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -10,6 +10,7 @@ #include "exec/watchpoint.h" #include "system/kvm.h" #include "system/kvm_xen.h" +#include "system/mshv.h" #include "system/tcg.h" #include "qemu/error-report.h" @@ -952,6 +953,24 @@ static const VMStateDescription vmstate_msr_hyperv_reenlightenment = { }; #ifdef CONFIG_MSHV + +static bool mshv_synthetic_timers_needed(void *opaque) +{ + /* Always migrate synthetic timers */ + return mshv_enabled(); +} + +static const VMStateDescription vmstate_mshv_synthetic_timers = { + .name = "cpu/mshv_synthetic_timers", + .version_id = 1, + .minimum_version_id = 1, + .needed = mshv_synthetic_timers_needed, + .fields = (const VMStateField[]) { + VMSTATE_BUFFER(env.hv_synthetic_timers_state, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + static bool mshv_synic_vp_state_needed(void *opaque) { X86CPU *cpu = opaque; @@ -1941,6 +1960,7 @@ const VMStateDescription vmstate_x86_cpu = { #endif #ifdef CONFIG_MSHV &vmstate_mshv_synic_vp_state, + &vmstate_mshv_synthetic_timers, #endif NULL } diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index ea6a660d05..86d64c67fb 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -118,6 +118,12 @@ static int get_synic_state(CPUState *cpu) int cpu_fd = mshv_vcpufd(cpu); int ret; + ret = mshv_get_synthetic_timers(cpu_fd, env->hv_synthetic_timers_state); + if (ret < 0) { + error_report("failed to get synthetic timers"); + return -1; + } + /* SIMP/SIEFP can only be read when SynIC is enabled */ if (!mshv_synic_enabled(cpu)) { return 0; @@ -1420,6 +1426,12 @@ static int set_synic_state(const CPUState *cpu) int cpu_fd = mshv_vcpufd(cpu); int ret; + ret = mshv_set_synthetic_timers(cpu_fd, env->hv_synthetic_timers_state); + if (ret < 0) { + error_report("failed to set synthetic timers state"); + return -1; + } + /* SIMP/SIEFP can only be written when SynIC is enabled */ if (!mshv_synic_enabled(cpu)) { return 0; diff --git a/target/i386/mshv/synic.c b/target/i386/mshv/synic.c index 8f9fee6ed7..4c629adc3a 100644 --- a/target/i386/mshv/synic.c +++ b/target/i386/mshv/synic.c @@ -54,6 +54,57 @@ static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state) return 0; } +int mshv_get_synthetic_timers(int cpu_fd, uint8_t *state) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SYNTHETIC_TIMERS; + + ret = get_vp_state(cpu_fd, &args); + + if (ret < 0) { + qemu_vfree(buffer); + error_report("failed to get synthetic timers"); + return -1; + } + + memcpy(state, buffer, MSHV_STIMERS_STATE_SIZE); + qemu_vfree(buffer); + + return 0; +} + +int mshv_set_synthetic_timers(int cpu_fd, const uint8_t *state) +{ + int ret; + void *buffer; + struct mshv_get_set_vp_state args = {0}; + + buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE); + memset(buffer, 0, HV_HYP_PAGE_SIZE); + args.buf_ptr = (uint64_t)buffer; + args.buf_sz = HV_HYP_PAGE_SIZE; + args.type = MSHV_VP_STATE_SYNTHETIC_TIMERS; + + assert(state); + memcpy(buffer, state, MSHV_STIMERS_STATE_SIZE); + + ret = set_vp_state(cpu_fd, &args); + qemu_vfree(buffer); + + if (ret < 0) { + error_report("failed to set synthetic timers"); + return -1; + } + + return 0; +} + int mshv_get_simp(int cpu_fd, uint8_t *page) { int ret; From 49cd174b56ae5db509a075752a8a9e5247ebbc2e Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:31 +0200 Subject: [PATCH 565/860] accel/mshv: write synthetic MSRs after migration Write partition-wide synthetic MSRs. This ensures the hypercall page and SynIC facilities are set up before vCPUs attempt to use it. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20260710101534.664604-10-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 15 +++++++++++++++ include/hw/hyperv/hvgdk_mini.h | 3 +++ include/system/mshv_int.h | 1 + target/i386/mshv/mshv-cpu.c | 15 +++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 1516475f34..1ca1d4b54f 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -60,6 +60,20 @@ static int init_mshv(int *mshv_fd) return 0; } +static int mshv_load_cleanup(void *opaque) +{ + int ret; + + ret = mshv_arch_set_partition_msrs(first_cpu); + if (ret < 0) { + error_report("Failed to set partition MSRs: %s", strerror(-ret)); + return -1; + } + + return 0; +} + + static int get_host_partition_property(int mshv_fd, uint32_t property_code, uint64_t *value) { @@ -496,6 +510,7 @@ static int mshv_init_vcpu(CPUState *cpu) } static SaveVMHandlers savevm_mshv = { + .load_cleanup = mshv_load_cleanup, }; static int mshv_init(AccelState *as, MachineState *ms) diff --git a/include/hw/hyperv/hvgdk_mini.h b/include/hw/hyperv/hvgdk_mini.h index 0602a7c6cc..a5527d49cf 100644 --- a/include/hw/hyperv/hvgdk_mini.h +++ b/include/hw/hyperv/hvgdk_mini.h @@ -23,6 +23,9 @@ #define HV_X64_MSR_APIC_FREQUENCY 0x40000023 typedef enum hv_register_name { + /* VP Management Registers */ + HV_REGISTER_INTERNAL_ACTIVITY_STATE = 0x00000004, + /* Pending Interruption Register */ HV_REGISTER_PENDING_INTERRUPTION = 0x00010002, HV_REGISTER_INTERRUPT_STATE = 0x00010003, diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index 063852115e..cbfcb8611b 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -98,6 +98,7 @@ int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, size_t n_regs); int mshv_arch_store_vcpu_state(const CPUState *cpu); int mshv_arch_load_vcpu_state(CPUState *cpu); +int mshv_arch_set_partition_msrs(const CPUState *cpu); void mshv_arch_init_vcpu(CPUState *cpu); void mshv_arch_destroy_vcpu(CPUState *cpu); void mshv_arch_amend_proc_features( diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 86d64c67fb..6bb3045752 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -1506,6 +1506,21 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu) return 0; } +int mshv_arch_set_partition_msrs(const CPUState *cpu) +{ + CPUX86State *env = &X86_CPU(cpu)->env; + struct hv_register_assoc assocs[] = { + { .name = HV_REGISTER_GUEST_OS_ID, + .value.reg64 = env->msr_hv_guest_os_id }, + { .name = HV_REGISTER_REFERENCE_TSC, + .value.reg64 = env->msr_hv_tsc }, + { .name = HV_X64_REGISTER_HYPERCALL, + .value.reg64 = env->msr_hv_hypercall }, + }; + + return mshv_set_generic_regs(cpu, assocs, ARRAY_SIZE(assocs)); +} + void mshv_arch_amend_proc_features( union hv_partition_synthetic_processor_features *features) { From ec38b3d54c4ff314670b923a50daf0bd7c040b8a Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:32 +0200 Subject: [PATCH 566/860] target/i386/mshv: migrate MP_STATE MSHV's "internal activity state" roughly maps to QEMU's env->mp_state and cpu->halted states that describe state of APs in a guest. We don't invoke set_mp_state as part of store_vcpu_state() b/c we would put all BSP + APs in a RUNNABLE (0) state immediately, breaking SMP boot Instead we store the mp state as part of the load_cleanup() routine after a migration. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20260710101534.664604-11-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- accel/mshv/mshv-all.c | 10 +++++ include/system/mshv_int.h | 1 + target/i386/mshv/mshv-cpu.c | 80 +++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c index 1ca1d4b54f..5921ce693e 100644 --- a/accel/mshv/mshv-all.c +++ b/accel/mshv/mshv-all.c @@ -62,6 +62,7 @@ static int init_mshv(int *mshv_fd) static int mshv_load_cleanup(void *opaque) { + CPUState *cpu; int ret; ret = mshv_arch_set_partition_msrs(first_cpu); @@ -70,6 +71,15 @@ static int mshv_load_cleanup(void *opaque) return -1; } + CPU_FOREACH(cpu) { + ret = mshv_arch_set_mp_state(cpu); + if (ret < 0) { + error_report("Failed to set mp state for vCPU %d: %s", + cpu->cpu_index, strerror(-ret)); + return -1; + } + } + return 0; } diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h index cbfcb8611b..3dffe3c5fb 100644 --- a/include/system/mshv_int.h +++ b/include/system/mshv_int.h @@ -99,6 +99,7 @@ int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs, int mshv_arch_store_vcpu_state(const CPUState *cpu); int mshv_arch_load_vcpu_state(CPUState *cpu); int mshv_arch_set_partition_msrs(const CPUState *cpu); +int mshv_arch_set_mp_state(const CPUState *cpu); void mshv_arch_init_vcpu(CPUState *cpu); void mshv_arch_destroy_vcpu(CPUState *cpu); void mshv_arch_amend_proc_features( diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c index 6bb3045752..f528dd2b9a 100644 --- a/target/i386/mshv/mshv-cpu.c +++ b/target/i386/mshv/mshv-cpu.c @@ -35,6 +35,11 @@ #include +#define MSHV_MP_STATE_RUNNABLE 0 +#define MSHV_MP_STATE_UNINITIALIZED 1 +#define MSHV_MP_STATE_INIT_RECEIVED 2 +#define MSHV_MP_STATE_HALTED 3 + #define MAX_REGISTER_COUNT (MAX_CONST(ARRAY_SIZE(STANDARD_REGISTER_NAMES), \ MAX_CONST(ARRAY_SIZE(SPECIAL_REGISTER_NAMES), \ ARRAY_SIZE(FPU_REGISTER_NAMES)))) @@ -950,6 +955,76 @@ static int set_vcpu_events(const CPUState *cpu) return 0; } +static int get_mp_state(CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + struct hv_register_assoc assoc = { + .name = HV_REGISTER_INTERNAL_ACTIVITY_STATE, + }; + union hv_internal_activity_register activity; + int ret; + + ret = mshv_get_generic_regs(cpu, &assoc, 1); + if (ret < 0) { + error_report("failed to get internal activity state"); + return -1; + } + + activity.as_uint64 = assoc.value.reg64; + + /* + * map MSHV activity state to KVM mp_state values, which are used as the + * shared representation in env->mp_state and serialized by vmstate_x86_cpu. + */ + + if (activity.startup_suspend) { + env->mp_state = MSHV_MP_STATE_UNINITIALIZED; + } else if (activity.halt_suspend) { + env->mp_state = MSHV_MP_STATE_HALTED; + } else { + env->mp_state = MSHV_MP_STATE_RUNNABLE; + } + + cpu->halted = (env->mp_state == MSHV_MP_STATE_HALTED); + + return 0; +} + +int mshv_arch_set_mp_state(const CPUState *cpu) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + union hv_internal_activity_register activity = { 0 }; + struct hv_register_assoc assoc = { + .name = HV_REGISTER_INTERNAL_ACTIVITY_STATE, + }; + int ret; + + switch (env->mp_state) { + case MSHV_MP_STATE_HALTED: + activity.halt_suspend = 1; + break; + case MSHV_MP_STATE_UNINITIALIZED: + case MSHV_MP_STATE_INIT_RECEIVED: + activity.startup_suspend = 1; + break; + case MSHV_MP_STATE_RUNNABLE: + default: + break; + } + + assoc.value.reg64 = activity.as_uint64; + + ret = mshv_set_generic_regs(cpu, &assoc, 1); + if (ret < 0) { + error_report("failed to set internal activity state"); + return -1; + } + + return 0; +} + static int update_hflags(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); @@ -1012,6 +1087,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu) return ret; } + ret = get_mp_state(cpu); + if (ret < 0) { + return ret; + } + return 0; } From 24bf07b6e2e3fc99c65035ffa778951cbcbfb475 Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Fri, 10 Jul 2026 12:15:34 +0200 Subject: [PATCH 567/860] hw/i386/mshv: drop initial msi vector 0 This has been a warning before that was always raised if the machine has a hpet. hpet_reset() will eventually result in mshv_send_msi called w/ vector 0, which we can safely drop. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20260710101534.664604-13-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- hw/i386/mshv/apic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/i386/mshv/apic.c b/hw/i386/mshv/apic.c index 9188a93b2f..ad326030bf 100644 --- a/hw/i386/mshv/apic.c +++ b/hw/i386/mshv/apic.c @@ -293,6 +293,16 @@ static void mshv_send_msi(MSIMessage *msi) delivery = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & MSI_DATA_DELIVERY_MODE_MASK; + /* + * Vector 0 is not a valid interrupt vector (0-15 are reserved for CPU + * exceptions). This can trigger during machine reset, if hpet_reset() + * forces the PIT to pulse GSI 2 before IOAPIC's own reset has masked its + * redirection entries. + */ + if (vector == 0) { + return; + } + mshv_request_interrupt(mshv_state, delivery, vector, dest, dest_mode, trigger_mode); } From 4196b5f0a0c01581418263fb8d9f63a179741b2c Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:57:54 +0530 Subject: [PATCH 568/860] target/i386: SEV: Generalize handling of SVM_SEV_FEAT_SNP_ACTIVE Align with IGVM files providing SEV features with SVM_SEV_FEAT_SNP_ACTIVE set by setting the same when creating a sev-snp-guest object. Since KVM sets this feature itself, SVM_SEV_FEAT_SNP_ACTIVE is unset before KVM_SEV_INIT2 ioctl is invoked. Move that out of IGVM-specific section to common code. While at it, convert the existing SVM_SEV_FEAT_SNP_ACTIVE definition to use the BIT() macro for consistency with upcoming feature flags. Reviewed-by: Tom Lendacky Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/031de849edf2ae4eaa6e00df83b053605a3ecfea.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 24 +++++++++++++++++------- target/i386/sev.h | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index d7dcefc8ec..7b03f534b1 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -324,6 +324,15 @@ sev_set_guest_state(SevCommonState *sev_common, SevState new_state) sev_common->state = new_state; } +static void sev_set_feature(SevCommonState *sev_common, uint64_t feature, bool set) +{ + if (set) { + sev_common->sev_features |= feature; + } else { + sev_common->sev_features &= ~feature; + } +} + static void sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, size_t max_size) @@ -1899,15 +1908,15 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) ->process(x86machine->igvm, machine, true, errp) == -1) { return -1; } - /* - * KVM maintains a bitmask of allowed sev_features. This does not - * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM - * itself. Therefore we need to clear this flag. - */ - args.vmsa_features = sev_common->sev_features & - ~SVM_SEV_FEAT_SNP_ACTIVE; } + /* + * KVM maintains a bitmask of allowed sev_features. This does not + * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM + * itself. Therefore we need to clear this flag. + */ + args.vmsa_features = sev_common->sev_features & ~SVM_SEV_FEAT_SNP_ACTIVE; + ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error); break; } @@ -3211,6 +3220,7 @@ sev_snp_guest_instance_init(Object *obj) /* default init/start/finish params for kvm */ sev_snp_guest->kvm_start_conf.policy = DEFAULT_SEV_SNP_POLICY; + sev_set_feature(SEV_COMMON(sev_snp_guest), SVM_SEV_FEAT_SNP_ACTIVE, true); } static void diff --git a/target/i386/sev.h b/target/i386/sev.h index 4358df40e4..b84ca3ce0b 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -46,7 +46,7 @@ bool sev_snp_enabled(void); #define SEV_SNP_POLICY_SMT 0x10000 #define SEV_SNP_POLICY_DBG 0x80000 -#define SVM_SEV_FEAT_SNP_ACTIVE 1 +#define SVM_SEV_FEAT_SNP_ACTIVE BIT(0) typedef struct SevKernelLoaderContext { char *setup_data; From a593e468dac5e3d7815eba8993304909efb16f9e Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:57:55 +0530 Subject: [PATCH 569/860] target/i386: SEV: Ensure SEV features are only set through qemu cli or IGVM In preparation for qemu being able to set SEV features through the cli, add a check to ensure that SEV features are not also set if using IGVM files. Reviewed-by: Tom Lendacky Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/6939de99f13d7170af68b74e711eb9f03f32f682.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index 7b03f534b1..093d29e761 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1904,6 +1904,16 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * as SEV_STATE_UNINIT. */ if (x86machine->igvm) { + /* + * Test only the user-set SEV features by masking out + * SVM_SEV_FEAT_SNP_ACTIVE which is set by default. + */ + if (sev_common->sev_features & ~SVM_SEV_FEAT_SNP_ACTIVE) { + error_setg(errp, + "%s: SEV features can't be specified when using IGVM files", + __func__); + return -1; + } if (IGVM_CFG_GET_CLASS(x86machine->igvm) ->process(x86machine->igvm, machine, true, errp) == -1) { return -1; From 9743cd30f33ff9ab5338efc3801505f125280587 Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:57:56 +0530 Subject: [PATCH 570/860] target/i386: SEV: Consolidate SEV feature validation to common init path Currently, check_sev_features() is called in multiple places when processing IGVM files: both when processing the initial VMSA SEV features from IGVM, as well as when validating the full contents of the VMSA. Move this to a single point in sev_common_kvm_init() to simplify the flow, as well as to re-use this function when VMSA SEV features are being set without using IGVM files. Reviewed-by: Tom Lendacky Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/35449df94eb20c29923a7cd0e2742ddba605928c.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 093d29e761..d8fb88d2b2 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -600,9 +600,6 @@ static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, vmsa_check.x87_fcw = 0; vmsa_check.mxcsr = 0; - if (check_sev_features(sev_common, vmsa_check.sev_features, errp) < 0) { - return -1; - } vmsa_check.sev_features = 0; if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { @@ -1920,6 +1917,10 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } } + if (check_sev_features(sev_common, sev_common->sev_features, errp) < 0) { + return -1; + } + /* * KVM maintains a bitmask of allowed sev_features. This does not * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM @@ -2581,9 +2582,6 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, __func__); return -1; } - if (check_sev_features(sev_common, sa->sev_features, errp) < 0) { - return -1; - } sev_common->sev_features = sa->sev_features; } return 0; From c93800f544dbac9ea7bf78cd802ab0731224b27f Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:57:57 +0530 Subject: [PATCH 571/860] target/i386: SEV: Validate that SEV-ES is enabled when VMSA features are used SEV features in the VMSA are only meaningful for SEV-ES and SEV-SNP guests, as they control aspects of the encrypted guest state that are not relevant for basic SEV guests. Add a check in check_sev_features() to ensure that SEV-ES or SEV-SNP is enabled when any SEV features are specified. Reviewed-by: Nikunj A Dadhania Reviewed-by: Tom Lendacky Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/11e34ae3db91643e45e097404d1aa949a820aa0d.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index d8fb88d2b2..52379855b3 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -523,6 +523,12 @@ static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, __func__); return -1; } + if (sev_features && !sev_es_enabled()) { + error_setg(errp, + "%s: SEV features require either SEV-ES or SEV-SNP to be enabled", + __func__); + return -1; + } if (sev_features & ~sev_common->supported_sev_features) { error_setg(errp, "%s: VMSA contains unsupported sev_features: %lX, " From 891d419f972f96b19ef7531dc72c11c302af5f35 Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:57:58 +0530 Subject: [PATCH 572/860] target/i386: SEV: Enable use of KVM_SEV_INIT2 for SEV-ES guests In preparation for allowing SEV-ES guests to enable VMSA SEV features, update sev_init2_required() to return true if any SEV features are requested. This enables qemu to use KVM_SEV_INIT2 for SEV-ES guests when necessary. Reviewed-by: Nikunj A Dadhania Reviewed-by: Tom Lendacky Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/f2a7778ab26b11a8de90e170ff984ccd29dc05a0.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 52379855b3..9cdccc872a 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -1697,8 +1697,7 @@ sev_vm_state_change(void *opaque, bool running, RunState state) */ static bool sev_init2_required(SevGuestState *sev_guest) { - /* Currently no KVM_SEV_INIT2-specific options are exposed via QEMU */ - return false; + return !!SEV_COMMON(sev_guest)->sev_features; } static int sev_kvm_type(X86ConfidentialGuest *cg) From 51ac358fdeab4e84b0ba8a3e38123eba7054e50d Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:57:59 +0530 Subject: [PATCH 573/860] target/i386: SEV: Add support for enabling debug-swap SEV feature Add support for enabling debug-swap VMSA SEV feature in SEV-ES and SEV-SNP guests through a new "debug-swap" boolean property on SEV guest objects. Though the boolean property is available for plain SEV guests, check_sev_features() has a check that rejects attempts to enable any SEV feature for a plain SEV guest. Though this SEV feature is called "Debug virtualization" in the APM, KVM calls this "debug swap" so use the same name for consistency. Sample command-line: -machine q35,confidential-guest-support=sev0 \ -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,debug-swap=on Restrict debug-swap to SEV-SNP guests at this time due to a compatibility issue with SEV-ES pflash devices. Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/416e7b156e49f95958f8c5c8549b48a88c1995fc.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- qapi/qom.json | 7 ++++++- target/i386/sev.c | 26 ++++++++++++++++++++++++++ target/i386/sev.h | 1 + 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/qapi/qom.json b/qapi/qom.json index c55776af7d..ad7e39aa75 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1017,6 +1017,10 @@ # designated guest firmware page for measured boot with -kernel # (default: false) (since 6.2) # +# @debug-swap: enable virtualization of debug registers, +# only supported on SEV-ES and SEV-SNP guests +# (default: false) (since 11.1) +# # Features: # # @confidential-guest-reset: If present, the hypervisor supports @@ -1028,7 +1032,8 @@ 'data': { '*sev-device': 'str', '*cbitpos': 'uint32', 'reduced-phys-bits': 'uint32', - '*kernel-hashes': 'bool' }, + '*kernel-hashes': 'bool', + '*debug-swap': 'bool' }, 'features': ['confidential-guest-reset']} ## diff --git a/target/i386/sev.c b/target/i386/sev.c index 9cdccc872a..2103a121aa 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -324,6 +324,11 @@ sev_set_guest_state(SevCommonState *sev_common, SevState new_state) sev_common->state = new_state; } +static bool is_sev_feature_set(SevCommonState *sev_common, uint64_t feature) +{ + return !!(sev_common->sev_features & feature); +} + static void sev_set_feature(SevCommonState *sev_common, uint64_t feature, bool set) { if (set) { @@ -523,6 +528,12 @@ static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, __func__); return -1; } + if (sev_features && sev_es_enabled() && !sev_snp_enabled()) { + error_setg(errp, + "%s: SEV features are not supported with SEV-ES at this time", + __func__); + return -1; + } if (sev_features && !sev_es_enabled()) { error_setg(errp, "%s: SEV features require either SEV-ES or SEV-SNP to be enabled", @@ -2797,6 +2808,16 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, return 0; } +static bool sev_common_get_debug_swap(Object *obj, Error **errp) +{ + return is_sev_feature_set(SEV_COMMON(obj), SVM_SEV_FEAT_DEBUG_SWAP); +} + +static void sev_common_set_debug_swap(Object *obj, bool value, Error **errp) +{ + sev_set_feature(SEV_COMMON(obj), SVM_SEV_FEAT_DEBUG_SWAP, value); +} + static void sev_common_class_init(ObjectClass *oc, const void *data) { @@ -2822,6 +2843,11 @@ sev_common_class_init(ObjectClass *oc, const void *data) sev_common_set_kernel_hashes); object_class_property_set_description(oc, "kernel-hashes", "add kernel hashes to guest firmware for measured Linux boot"); + object_class_property_add_bool(oc, "debug-swap", + sev_common_get_debug_swap, + sev_common_set_debug_swap); + object_class_property_set_description(oc, "debug-swap", + "enable virtualization of debug registers"); } static void diff --git a/target/i386/sev.h b/target/i386/sev.h index b84ca3ce0b..d19a396697 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -47,6 +47,7 @@ bool sev_snp_enabled(void); #define SEV_SNP_POLICY_DBG 0x80000 #define SVM_SEV_FEAT_SNP_ACTIVE BIT(0) +#define SVM_SEV_FEAT_DEBUG_SWAP BIT(5) typedef struct SevKernelLoaderContext { char *setup_data; From a26f9b975d5c0690ac5828fadb6242bfee79c008 Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:58:00 +0530 Subject: [PATCH 574/860] target/i386: SEV: Add support for enabling Secure TSC SEV feature Add support for enabling Secure TSC VMSA SEV feature in SEV-SNP guests through a new "secure-tsc" boolean property on SEV-SNP guest objects. By default, KVM uses the host TSC frequency for Secure TSC. Sample command-line: -machine q35,confidential-guest-support=sev0 \ -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,secure-tsc=on Reviewed-by: Tom Lendacky Co-developed-by: Ketan Chaturvedi Signed-off-by: Ketan Chaturvedi Co-developed-by: Nikunj A Dadhania Signed-off-by: Nikunj A Dadhania Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/9f58b92a173f319b3ef725f5ed8a2a173eed55b1.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- qapi/qom.json | 6 +++++- target/i386/sev.c | 13 +++++++++++++ target/i386/sev.h | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/qapi/qom.json b/qapi/qom.json index ad7e39aa75..4732fa8975 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1113,6 +1113,9 @@ # firmware. Set this to true to disable the use of VCEK. # (default: false) (since: 9.1) # +# @secure-tsc: enable Secure TSC +# (default: false) (since 11.1) +# # Since: 9.1 ## { 'struct': 'SevSnpGuestProperties', @@ -1124,7 +1127,8 @@ '*id-auth': 'str', '*author-key-enabled': 'bool', '*host-data': 'str', - '*vcek-disabled': 'bool' } } + '*vcek-disabled': 'bool', + '*secure-tsc': 'bool' } } ## # @TdxGuestProperties: diff --git a/target/i386/sev.c b/target/i386/sev.c index 2103a121aa..fdfeb604f6 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -3212,6 +3212,16 @@ sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp) memcpy(finish->host_data, blob, len); } +static bool sev_snp_guest_get_secure_tsc(Object *obj, Error **errp) +{ + return is_sev_feature_set(SEV_COMMON(obj), SVM_SEV_FEAT_SECURE_TSC); +} + +static void sev_snp_guest_set_secure_tsc(Object *obj, bool value, Error **errp) +{ + sev_set_feature(SEV_COMMON(obj), SVM_SEV_FEAT_SECURE_TSC, value); +} + static void sev_snp_guest_class_init(ObjectClass *oc, const void *data) { @@ -3247,6 +3257,9 @@ sev_snp_guest_class_init(ObjectClass *oc, const void *data) object_class_property_add_str(oc, "host-data", sev_snp_guest_get_host_data, sev_snp_guest_set_host_data); + object_class_property_add_bool(oc, "secure-tsc", + sev_snp_guest_get_secure_tsc, + sev_snp_guest_set_secure_tsc); } static void diff --git a/target/i386/sev.h b/target/i386/sev.h index d19a396697..7725f92e19 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -48,6 +48,7 @@ bool sev_snp_enabled(void); #define SVM_SEV_FEAT_SNP_ACTIVE BIT(0) #define SVM_SEV_FEAT_DEBUG_SWAP BIT(5) +#define SVM_SEV_FEAT_SECURE_TSC BIT(9) typedef struct SevKernelLoaderContext { char *setup_data; From f79c89c296896d97f5c6dc4f1635dbbe897bfdbc Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:58:01 +0530 Subject: [PATCH 575/860] target/i386: SEV: Add support for setting TSC frequency for Secure TSC Add support for configuring the TSC frequency when Secure TSC is enabled in SEV-SNP guests through a new "tsc-frequency" property on SEV-SNP guest objects, similar to the vCPU-specific property used by regular guests and TDX. A new property is needed since SEV-SNP guests require the TSC frequency to be specified during early SNP_LAUNCH_START command before any vCPUs are created. The user-provided TSC frequency is set through KVM_SET_TSC_KHZ before issuing KVM_SEV_SNP_LAUNCH_START. Attempts to set TSC frequency on both the SEV_SNP object and the cpu object result in an error from KVM (on the vCPU ioctl), so do not add separate checks for the same. Sample command-line: -machine q35,confidential-guest-support=sev0 \ -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,secure-tsc=on,tsc-frequency=2500000000 Co-developed-by: Ketan Chaturvedi Signed-off-by: Ketan Chaturvedi Co-developed-by: Nikunj A Dadhania Signed-off-by: Nikunj A Dadhania Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/af688610978f213a456a7753e1d9fe7d3a51e80a.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- qapi/qom.json | 6 +++++- target/i386/sev.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/qapi/qom.json b/qapi/qom.json index 4732fa8975..4a9b7f9088 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1116,6 +1116,9 @@ # @secure-tsc: enable Secure TSC # (default: false) (since 11.1) # +# @tsc-frequency: set secure TSC frequency. Only valid if Secure TSC +# is enabled (default: zero) (since 11.1) +# # Since: 9.1 ## { 'struct': 'SevSnpGuestProperties', @@ -1128,7 +1131,8 @@ '*author-key-enabled': 'bool', '*host-data': 'str', '*vcek-disabled': 'bool', - '*secure-tsc': 'bool' } } + '*secure-tsc': 'bool', + '*tsc-frequency': 'uint32' } } ## # @TdxGuestProperties: diff --git a/target/i386/sev.c b/target/i386/sev.c index fdfeb604f6..ae9c85af13 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -183,6 +183,7 @@ struct SevSnpGuestState { char *id_auth_base64; uint8_t *id_auth; char *host_data; + uint32_t tsc_khz; struct kvm_sev_snp_launch_start kvm_start_conf; struct kvm_sev_snp_launch_finish kvm_finish_conf; @@ -547,6 +548,13 @@ static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, __func__, sev_features, sev_common->supported_sev_features); return -1; } + if (sev_snp_enabled() && SEV_SNP_GUEST(sev_common)->tsc_khz && + !(sev_features & SVM_SEV_FEAT_SECURE_TSC)) { + error_setg(errp, + "%s: TSC frequency can only be set if Secure TSC is enabled", + __func__); + return -1; + } return 0; } @@ -1095,6 +1103,19 @@ sev_snp_launch_start(SevCommonState *sev_common) return 1; } + if (is_sev_feature_set(sev_common, SVM_SEV_FEAT_SECURE_TSC) && + sev_snp_guest->tsc_khz) { + rc = -EINVAL; + if (kvm_check_extension(kvm_state, KVM_CAP_VM_TSC_CONTROL)) { + rc = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, sev_snp_guest->tsc_khz); + } + if (rc < 0) { + error_report("%s: Unable to set Secure TSC frequency to %u kHz ret=%d", + __func__, sev_snp_guest->tsc_khz, rc); + return 1; + } + } + rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_START, start, &fw_error); if (rc < 0) { @@ -3222,6 +3243,28 @@ static void sev_snp_guest_set_secure_tsc(Object *obj, bool value, Error **errp) sev_set_feature(SEV_COMMON(obj), SVM_SEV_FEAT_SECURE_TSC, value); } +static void +sev_snp_guest_get_tsc_frequency(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value = SEV_SNP_GUEST(obj)->tsc_khz * 1000; + + visit_type_uint32(v, name, &value, errp); +} + +static void +sev_snp_guest_set_tsc_frequency(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + SEV_SNP_GUEST(obj)->tsc_khz = value / 1000; +} + static void sev_snp_guest_class_init(ObjectClass *oc, const void *data) { @@ -3260,6 +3303,9 @@ sev_snp_guest_class_init(ObjectClass *oc, const void *data) object_class_property_add_bool(oc, "secure-tsc", sev_snp_guest_get_secure_tsc, sev_snp_guest_set_secure_tsc); + object_class_property_add(oc, "tsc-frequency", "uint32", + sev_snp_guest_get_tsc_frequency, + sev_snp_guest_set_tsc_frequency, NULL, NULL); } static void From feed8bdfdc1a292fa2b6fe993cb5a9e8f2190e1f Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Wed, 20 May 2026 18:58:02 +0530 Subject: [PATCH 576/860] target/i386: SEV: Refactor check_sev_features() Refactor check_sev_features() to consolidate SEV-SNP checks to a single if block. This is also helpful when adding checks for future SEV features. While at it, move the comment about the checks being done outside of the function body and expand it to describe what this function does. Update error_setg() invocations to use a consistent format. No functional change intended. Suggested-by: Tom Lendacky Signed-off-by: Naveen N Rao (AMD) Link: https://lore.kernel.org/r/cae04d88adfbcdc2997e518475f3b89091adf8a9.1779281646.git.naveen@kernel.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 67 +++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index ae9c85af13..8acbb01da2 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -507,40 +507,22 @@ static void sev_apply_cpu_context(CPUState *cpu) } } +/* + * Ensure SEV_FEATURES is configured for correct SEV hardware and that + * the requested features are supported. In addition, ensure feature + * dependencies are satisfied (allow tsc-frequency only if secure-tsc + * is also enabled, as an example). + */ static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, Error **errp) { - /* - * Ensure SEV_FEATURES is configured for correct SEV hardware and that - * the requested features are supported. If SEV-SNP is enabled then - * that feature must be enabled, otherwise it must be cleared. - */ - if (sev_snp_enabled() && !(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { - error_setg( - errp, - "%s: SEV_SNP is enabled but is not enabled in VMSA sev_features", - __func__); - return -1; - } else if (!sev_snp_enabled() && - (sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { - error_setg( - errp, - "%s: SEV_SNP is not enabled but is enabled in VMSA sev_features", - __func__); - return -1; - } - if (sev_features && sev_es_enabled() && !sev_snp_enabled()) { - error_setg(errp, - "%s: SEV features are not supported with SEV-ES at this time", - __func__); - return -1; - } if (sev_features && !sev_es_enabled()) { error_setg(errp, "%s: SEV features require either SEV-ES or SEV-SNP to be enabled", __func__); return -1; } + if (sev_features & ~sev_common->supported_sev_features) { error_setg(errp, "%s: VMSA contains unsupported sev_features: %lX, " @@ -548,13 +530,36 @@ static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, __func__, sev_features, sev_common->supported_sev_features); return -1; } - if (sev_snp_enabled() && SEV_SNP_GUEST(sev_common)->tsc_khz && - !(sev_features & SVM_SEV_FEAT_SECURE_TSC)) { - error_setg(errp, - "%s: TSC frequency can only be set if Secure TSC is enabled", - __func__); - return -1; + + if (sev_snp_enabled()) { + if (!(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg(errp, + "%s: SEV_SNP is enabled but is not enabled in VMSA sev_features", + __func__); + return -1; + } + if (SEV_SNP_GUEST(sev_common)->tsc_khz && + !(sev_features & SVM_SEV_FEAT_SECURE_TSC)) { + error_setg(errp, + "%s: TSC frequency can only be set if Secure TSC is enabled", + __func__); + return -1; + } + } else { + if (sev_features && sev_es_enabled()) { + error_setg(errp, + "%s: SEV features are not supported with SEV-ES at this time", + __func__); + return -1; + } + if (sev_features & SVM_SEV_FEAT_SNP_ACTIVE) { + error_setg(errp, + "%s: SEV_SNP is not enabled but is enabled in VMSA sev_features", + __func__); + return -1; + } } + return 0; } From 5be66f90bdf180d70a3f5d4ab2de399b5190a851 Mon Sep 17 00:00:00 2001 From: "Naveen N Rao (AMD)" Date: Fri, 26 Jun 2026 12:30:10 +0530 Subject: [PATCH 577/860] target/i386: SEV: Remove use of __func__ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove all usages of __func__ in target/i386/sev.c to align with the general QEMU preference, and replace those with "SEV:" prefix. Suggested-by: Daniel P. Berrangé Signed-off-by: Naveen N Rao (AMD) Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20260626070010.1955433-1-naveen@kernel.org Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 155 +++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 86 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 8acbb01da2..4d875d10ff 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -363,8 +363,8 @@ sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size, trace_kvm_memcrypt_register_region(host, max_size); r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_REG_REGION, &range); if (r) { - error_report("%s: failed to register region (%p+%#zx) error '%s'", - __func__, host, max_size, strerror(errno)); + error_report("SEV: Failed to register region (%p+%#zx) error '%s'", + host, max_size, strerror(errno)); exit(1); } } @@ -393,8 +393,8 @@ sev_ram_block_removed(RAMBlockNotifier *n, void *host, size_t size, trace_kvm_memcrypt_unregister_region(host, max_size); r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_UNREG_REGION, &range); if (r) { - error_report("%s: failed to unregister region (%p+%#zx)", - __func__, host, max_size); + error_report("SEV: Failed to unregister region (%p+%#zx)", + host, max_size); } } @@ -518,44 +518,39 @@ static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, { if (sev_features && !sev_es_enabled()) { error_setg(errp, - "%s: SEV features require either SEV-ES or SEV-SNP to be enabled", - __func__); + "SEV: SEV features require either SEV-ES or SEV-SNP to be enabled"); return -1; } if (sev_features & ~sev_common->supported_sev_features) { error_setg(errp, - "%s: VMSA contains unsupported sev_features: %lX, " + "SEV: VMSA contains unsupported sev_features: %lX, " "supported features: %lX", - __func__, sev_features, sev_common->supported_sev_features); + sev_features, sev_common->supported_sev_features); return -1; } if (sev_snp_enabled()) { if (!(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { error_setg(errp, - "%s: SEV_SNP is enabled but is not enabled in VMSA sev_features", - __func__); + "SEV: SEV_SNP is enabled but is not enabled in VMSA sev_features"); return -1; } if (SEV_SNP_GUEST(sev_common)->tsc_khz && !(sev_features & SVM_SEV_FEAT_SECURE_TSC)) { error_setg(errp, - "%s: TSC frequency can only be set if Secure TSC is enabled", - __func__); + "SEV: TSC frequency can only be set if Secure TSC is enabled"); return -1; } } else { if (sev_features && sev_es_enabled()) { error_setg(errp, - "%s: SEV features are not supported with SEV-ES at this time", - __func__); + "SEV: SEV features are not supported with SEV-ES at this time"); return -1; } if (sev_features & SVM_SEV_FEAT_SNP_ACTIVE) { error_setg(errp, - "%s: SEV_SNP is not enabled but is enabled in VMSA sev_features", - __func__); + "SEV: SEV_SNP is not enabled but is enabled in VMSA sev_features"); return -1; } } @@ -578,8 +573,8 @@ static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, */ if (gpa != KVM_VMSA_GPA) { error_setg(errp, - "%s: The VMSA GPA must be %lX but is specified as %lX", - __func__, KVM_VMSA_GPA, gpa); + "SEV: The VMSA GPA must be %lX but is specified as %lX", + KVM_VMSA_GPA, gpa); return -1; } @@ -634,11 +629,10 @@ static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { error_setg(errp, - "%s: The VMSA contains fields that are not " + "SEV: The VMSA contains fields that are not " "synchronized with KVM. Continuing would result in " "either unpredictable guest behavior, or a " - "mismatched launch measurement.", - __func__); + "mismatched launch measurement."); return -1; } return 0; @@ -1115,8 +1109,8 @@ sev_snp_launch_start(SevCommonState *sev_common) rc = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, sev_snp_guest->tsc_khz); } if (rc < 0) { - error_report("%s: Unable to set Secure TSC frequency to %u kHz ret=%d", - __func__, sev_snp_guest->tsc_khz, rc); + error_report("SEV: Unable to set Secure TSC frequency to %u kHz ret=%d", + sev_snp_guest->tsc_khz, rc); return 1; } } @@ -1124,8 +1118,8 @@ sev_snp_launch_start(SevCommonState *sev_common) rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_SNP_LAUNCH_START, start, &fw_error); if (rc < 0) { - error_report("%s: SNP_LAUNCH_START ret=%d fw_error=%d '%s'", - __func__, rc, fw_error, fw_error_to_str(fw_error)); + error_report("SEV: SNP_LAUNCH_START ret=%d fw_error=%d '%s'", + rc, fw_error, fw_error_to_str(fw_error)); return 1; } @@ -1167,8 +1161,8 @@ sev_launch_start(SevCommonState *sev_common) trace_kvm_sev_launch_start(start.policy, session, dh_cert); rc = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_START, &start, &fw_error); if (rc < 0) { - error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_report("SEV: LAUNCH_START ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); goto out; } @@ -1341,7 +1335,7 @@ static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { - error_setg(errp, "%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", __func__, + error_setg(errp, "SEV: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", ret, fw_error, fw_error_to_str(fw_error)); } @@ -1366,8 +1360,8 @@ sev_launch_update_vmsa(SevGuestState *sev_guest) ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); if (ret) { - error_report("%s: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_report("SEV: LAUNCH_UPDATE_VMSA ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); } return ret; @@ -1399,8 +1393,8 @@ sev_launch_get_measure(Notifier *notifier, void *unused) ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (!measurement.len) { - error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", - __func__, ret, error, fw_error_to_str(errno)); + error_report("SEV: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(errno)); return; } @@ -1411,8 +1405,8 @@ sev_launch_get_measure(Notifier *notifier, void *unused) ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_MEASURE, &measurement, &error); if (ret) { - error_report("%s: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", - __func__, ret, error, fw_error_to_str(errno)); + error_report("SEV: LAUNCH_MEASURE ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(errno)); return; } @@ -1467,8 +1461,8 @@ sev_launch_finish(SevCommonState *sev_common) ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_FINISH, 0, &error); if (ret) { - error_report("%s: LAUNCH_FINISH ret=%d fw_error=%d '%s'", - __func__, ret, error, fw_error_to_str(error)); + error_report("SEV: LAUNCH_FINISH ret=%d fw_error=%d '%s'", + ret, error, fw_error_to_str(error)); exit(1); } @@ -1627,8 +1621,8 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, hva = gpa2hva(&mr, desc->base, desc->len, NULL); if (!hva) { - error_report("%s: Failed to get HVA for GPA 0x%x sz 0x%x", - __func__, desc->base, desc->len); + error_report("SEV: Failed to get HVA for GPA 0x%x sz 0x%x", + desc->base, desc->len); exit(1); } @@ -1644,8 +1638,8 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, } if (ret) { - error_report("%s: Failed to add metadata page gpa 0x%x+%x type %d", - __func__, desc->base, desc->len, desc->type); + error_report("SEV: Failed to add metadata page gpa 0x%x+%x type %d", + desc->base, desc->len, desc->type); exit(1); } } @@ -1674,7 +1668,7 @@ sev_snp_launch_finish(SevCommonState *sev_common) */ metadata = pc_system_get_ovmf_sev_metadata_ptr(); if (metadata == NULL) { - error_report("%s: Failed to locate SEV metadata header", __func__); + error_report("SEV: SNP_LAUNCH_FINISH failed to locate SEV metadata header"); exit(1); } @@ -1812,8 +1806,7 @@ static int sev_init_supported_features(ConfidentialGuestSupport *cgs, .addr = (unsigned long)&sev_common->supported_sev_features, }; if (kvm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr) < 0) { - error_setg(errp, "%s: failed to query supported sev_features", - __func__); + error_setg(errp, "SEV: failed to query supported sev_features"); return -1; } if (sev_snp_enabled()) { @@ -1851,8 +1844,8 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) * comparison against the host value accomplishes that. */ if (host_cbitpos != sev_common->cbitpos) { - error_setg(errp, "%s: cbitpos check failed, host '%d' requested '%d'", - __func__, host_cbitpos, sev_common->cbitpos); + error_setg(errp, "SEV: cbitpos check failed, host '%d' requested '%d'", + host_cbitpos, sev_common->cbitpos); return -1; } @@ -1863,9 +1856,9 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) */ if (sev_common->reduced_phys_bits < 1 || sev_common->reduced_phys_bits > 63) { - error_setg(errp, "%s: reduced_phys_bits check failed," + error_setg(errp, "SEV: reduced_phys_bits check failed," " it should be in the range of 1 to 63, requested '%d'", - __func__, sev_common->reduced_phys_bits); + sev_common->reduced_phys_bits); return -1; } @@ -1881,8 +1874,8 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) ret = sev_platform_ioctl(sev_common->sev_fd, SEV_PLATFORM_STATUS, &status, &fw_error); if (ret) { - error_setg(errp, "%s: failed to get platform status ret=%d " - "fw_error='%d: %s'", __func__, ret, fw_error, + error_setg(errp, "SEV: failed to get platform status ret=%d " + "fw_error='%d: %s'", ret, fw_error, fw_error_to_str(fw_error)); return -1; } @@ -1892,17 +1885,15 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) if (sev_es_enabled()) { if (!kvm_kernel_irqchip_allowed()) { - error_setg(errp, "%s: SEV-ES guests require in-kernel irqchip" - "support", __func__); + error_setg(errp, "SEV: SEV-ES guests require in-kernel irqchip support"); return -1; } } if (sev_es_enabled() && !sev_snp_enabled()) { if (!(status.flags & SEV_STATUS_FLAGS_CONFIG_ES)) { - error_setg(errp, "%s: guest policy requires SEV-ES, but " - "host SEV-ES support unavailable", - __func__); + error_setg(errp, "SEV: guest policy requires SEV-ES, but " + "host SEV-ES support unavailable"); return -1; } } @@ -1949,8 +1940,7 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) */ if (sev_common->sev_features & ~SVM_SEV_FEAT_SNP_ACTIVE) { error_setg(errp, - "%s: SEV features can't be specified when using IGVM files", - __func__); + "SEV: SEV features can't be specified when using IGVM files"); return -1; } if (IGVM_CFG_GET_CLASS(x86machine->igvm) @@ -1974,21 +1964,20 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) break; } default: - error_setg(errp, "%s: host kernel does not support the requested SEV configuration.", - __func__); + error_setg(errp, "SEV: host kernel does not support the requested SEV configuration."); return -1; } if (ret) { - error_setg(errp, "%s: failed to initialize ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_setg(errp, "SEV: failed to initialize ret=%d fw_error=%d '%s'", + ret, fw_error, fw_error_to_str(fw_error)); return -1; } ret = klass->launch_start(sev_common); if (ret) { - error_setg(errp, "%s: failed to create encryption context", __func__); + error_setg(errp, "SEV: failed to create encryption context"); return -1; } @@ -2016,7 +2005,7 @@ static int sev_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) */ ret = ram_block_discard_disable(true); if (ret) { - error_setg(errp, "%s: cannot disable RAM discard", __func__); + error_setg(errp, "SEV: cannot disable RAM discard"); return -1; } @@ -2620,8 +2609,7 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, const struct sev_es_save_area *sa = (const struct sev_es_save_area *)ptr; if (len < sizeof(*sa)) { - error_setg(errp, "%s: invalid VMSA length encountered", - __func__); + error_setg(errp, "SEV: invalid VMSA length encountered"); return -1; } sev_common->sev_features = sa->sev_features; @@ -2630,8 +2618,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, } if (!sev_enabled()) { - error_setg(errp, "%s: attempt to configure guest memory, but SEV " - "is not enabled", __func__); + error_setg(errp, "SEV: attempt to configure guest memory, but SEV " + "is not enabled"); return -1; } @@ -2643,9 +2631,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, case CGS_PAGE_TYPE_VMSA: if (!sev_es_enabled()) { error_setg(errp, - "%s: attempt to configure initial VMSA, but SEV-ES " - "is not supported", - __func__); + "SEV: attempt to configure initial VMSA, but SEV-ES " + "is not supported"); return -1; } if (check_vmsa_supported(sev_common, gpa, @@ -2666,9 +2653,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, case CGS_PAGE_TYPE_SECRETS: if (!sev_snp_enabled()) { error_setg(errp, - "%s: attempt to configure secrets page, but SEV-SNP " - "is not supported", - __func__); + "SEV: attempt to configure secrets page, but SEV-SNP " + "is not supported"); return -1; } return snp_launch_update_data(gpa, ptr, len, @@ -2678,8 +2664,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, if (kvm_convert_memory(gpa, len, true) < 0) { error_setg( errp, - "%s: failed to configure required memory. gpa: %lX, type: %d", - __func__, gpa, memory_type); + "SEV: failed to configure required memory. gpa: %lX, type: %d", + gpa, memory_type); return -1; } return 0; @@ -2687,14 +2673,13 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, case CGS_PAGE_TYPE_CPUID: if (!sev_snp_enabled()) { error_setg(errp, - "%s: attempt to configure CPUID page, but SEV-SNP " - "is not supported", - __func__); + "SEV: attempt to configure CPUID page, but SEV-SNP " + "is not supported"); return -1; } return snp_launch_update_cpuid(gpa, ptr, len, errp); } - error_setg(errp, "%s: failed to update guest. gpa: %lX, type: %d", __func__, + error_setg(errp, "SEV: failed to update guest. gpa: %lX, type: %d", gpa, memory_type); return -1; } @@ -2750,8 +2735,8 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, } if (policy_type != GUEST_POLICY_SEV) { - error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d", - __func__, policy_type); + error_setg(errp, "SEV: Invalid guest policy type provided for SEV: %d", + policy_type); return -1; } /* @@ -2784,14 +2769,12 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, (struct sev_snp_id_authentication *)policy_data2; if (policy_data1_size != KVM_SEV_SNP_ID_BLOCK_SIZE) { - error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect size", - __func__); + error_setg(errp, "SEV: Invalid SEV-SNP ID block: incorrect size"); return -1; } if (policy_data2_size != KVM_SEV_SNP_ID_AUTH_SIZE) { error_setg(errp, - "%s: Invalid SEV-SNP ID auth block: incorrect size", - __func__); + "SEV: Invalid SEV-SNP ID auth block: incorrect size"); return -1; } assert(policy_data1 != NULL); @@ -2821,8 +2804,8 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs); /* Only the policy flags are supported for SEV and SEV-ES */ if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_guest) { - error_setg(errp, "%s: An ID block/ID auth block has been provided " - "but SEV-SNP is not enabled", __func__); + error_setg(errp, "SEV: An ID block/ID auth block has been provided " + "but SEV-SNP is not enabled"); return -1; } From 2667133c2799f670366b94059cc0513ae57fe8cb Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 23 Jun 2026 17:24:05 +0200 Subject: [PATCH 578/860] job: keep job paused across overlapping pause requests job_pause_point_locked() sets job->paused before yielding and clears it unconditionally on wake, before re-checking whether a pause is still pending. job_pause() re-enters a parked job only while it is not yet paused, so the wake that resumes one comes from a drain *ending* (job_resume() -> job_enter_cond()). If the next drain begins before that wake runs, the woken coroutine clears job->paused while pause_count is already > 0 again: AioContext change (BQL thread) job coroutine (iothread) ----------------------------- ------------------------ parked in job_pause_point(): paused=1, pause_count=1, yielded drain ends -> job_resume(): pause_count = 0 job_enter_cond(): queue wake ..> (wake pending) bdrv_try_change_aio_context(): bdrv_drain_all_begin(): job_pause() per node pause_count = N (> 0) wake runs, leaves job_do_yield(): paused = 0 (pause_count == N) tran_commit -> job_set_aio_context(): assert(paused || completed) --> abort: paused == 0 bdrv_try_change_aio_context() drains precisely to quiesce the job before changing its AioContext, but that brief paused==0 window trips the assertion. It is guest-triggerable: a virtio-blk reset (virtio_blk_stop_ioeventfd() -> blk_set_aio_context()) racing a running mirror/blockCopy job hits it, as do x-blockdev-set-iothread, blockdev hot-plug/unplug and job completion. Keep job->paused set while a pause is still pending: loop the yield until job_should_pause_locked() is false (or the job is cancelled), and only then clear job->paused. Drained-state consumers then never observe a pending-pause job as unpaused. Signed-off-by: Denis V. Lunev Message-ID: <20260623152406.1180235-2-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- job.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/job.c b/job.c index e747908472..d7220aaf30 100644 --- a/job.c +++ b/job.c @@ -629,7 +629,14 @@ static void coroutine_fn job_pause_point_locked(Job *job) ? JOB_STATUS_STANDBY : JOB_STATUS_PAUSED); job->paused = true; - job_do_yield_locked(job, -1); + /* + * Stay paused across back-to-back pause requests: a transient + * paused == false while pause_count > 0 would be observed as + * "not paused" by job_set_aio_context() and other drain consumers. + */ + do { + job_do_yield_locked(job, -1); + } while (job_should_pause_locked(job) && !job_is_cancelled_locked(job)); job->paused = false; job_state_transition_locked(job, status); } From 438ee08bc97af48ade6365133f2f5b43d4d788ae Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 23 Jun 2026 17:24:06 +0200 Subject: [PATCH 579/860] tests/unit/test-blockjob: cover keeping a job paused while a pause is pending Add a regression test for the previous commit. A job that has reached its pause point is spuriously re-entered (job_enter()) while a pause is still pending (pause_count > 0), reproducing what an overlapping drain does: one drain's job_resume() wakes the job while the next drain's job_pause() is already counted. The job must stay parked - it must not run job code or clear job->paused, or job_set_aio_context() could observe paused == false and abort. The test counts the job's run-loop iterations: without the fix the re-entered job clears job->paused, runs one iteration and re-pauses, so the counter advances; with the fix it stays parked and the counter is unchanged. It runs in the main AioContext, so job_enter() is synchronous and the check is deterministic. Signed-off-by: Denis V. Lunev Message-ID: <20260623152406.1180235-3-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Tested-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/unit/test-blockjob.c | 100 +++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index abdbe4b835..3b77d19b91 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -388,6 +388,105 @@ static void test_cancel_concluded(void) cancel_common(s); } +typedef struct PauseCountJob { + BlockJob common; + int n; + bool should_complete; +} PauseCountJob; + +static void pause_count_job_complete(Job *job, Error **errp) +{ + PauseCountJob *s = container_of(job, PauseCountJob, common.job); + s->should_complete = true; +} + +static int coroutine_fn pause_count_job_run(Job *job, Error **errp) +{ + PauseCountJob *s = container_of(job, PauseCountJob, common.job); + + while (!s->should_complete) { + if (job_is_cancelled(&s->common.job)) { + return 0; + } + s->n++; + /* + * Yields; while a pause is pending the yield is skipped and the job + * parks in job_pause_point() instead. + */ + job_sleep_ns(&s->common.job, 10 * 1000 * 1000); + } + + return 0; +} + +static const BlockJobDriver pause_count_job_driver = { + .job_driver = { + .instance_size = sizeof(PauseCountJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .run = pause_count_job_run, + .complete = pause_count_job_complete, + }, +}; + +/* + * A job that has reached its pause point must stay paused while a pause is + * still pending (pause_count > 0). An overlapping drain re-enters the job (one + * drain's job_resume() wakes it while the next drain's job_pause() is already + * counted); the job must not run or clear job->paused, otherwise + * job_set_aio_context() can observe paused == false and abort. + */ +static void test_pause_keeps_paused(void) +{ + BlockBackend *blk; + BlockJob *bjob; + PauseCountJob *s; + Job *job; + int n0; + + blk = create_blk(NULL); + bjob = mk_job(blk, "job0", &pause_count_job_driver, true, + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + s = container_of(bjob, PauseCountJob, common); + job = &bjob->job; + WITH_JOB_LOCK_GUARD() { + job_ref_locked(job); + } + + job_start(job); + + /* Pause the running job; it parks in job_pause_point() with paused set. */ + WITH_JOB_LOCK_GUARD() { + job_pause_locked(job); + g_assert_true(job->paused); + g_assert_cmpint(job->status, ==, JOB_STATUS_PAUSED); + } + n0 = s->n; + + /* + * Spurious wake while the pause is still pending. The job must stay parked: + * the bug clears job->paused, runs an iteration (s->n advances) and + * re-pauses, exposing a paused == false window. + */ + job_enter(job); + WITH_JOB_LOCK_GUARD() { + g_assert_true(job->paused); + } + g_assert_cmpint(s->n, ==, n0); + + /* Resume and tear down. */ + WITH_JOB_LOCK_GUARD() { + job_resume_locked(job); + } + job_cancel_sync(job, true); + WITH_JOB_LOCK_GUARD() { + Job *dummy = job; + job_dismiss_locked(&dummy, &error_abort); + job_unref_locked(job); + } + destroy_blk(blk); +} + int main(int argc, char **argv) { qemu_init_main_loop(&error_abort); @@ -402,5 +501,6 @@ int main(int argc, char **argv) g_test_add_func("/blockjob/cancel/standby", test_cancel_standby); g_test_add_func("/blockjob/cancel/pending", test_cancel_pending); g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded); + g_test_add_func("/blockjob/pause/keep_paused", test_pause_keeps_paused); return g_test_run(); } From 59ecd8a1ac3f05f572155a2d7a81fbeec397e2d3 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 16 Jul 2026 13:22:40 +0200 Subject: [PATCH 580/860] block/monitor: reject persistent bitmap add on a read-only node qmp_block_dirty_bitmap_add() marks a new bitmap persistent without checking write access to its node. bdrv_create_dirty_bitmap() always creates bitmaps writable, so a persistent bitmap added to an already read-only node stays writable in memory on a node that can never store it, and the next global inactivation fails: Lost persistent bitmaps during inactivation of node '': No write access migration_block_inactivate: bdrv_inactivate_all() failed: -22 Forcing it read-only instead does not help: it was never stored, so it stays unpromotable on the next reopen to read-write and can trip bdrv_set_dirty()'s readonly assert on the first write. Reject the add instead, for both read-only and inactive nodes -- an already-inactive node skips qcow2_inactivate() on close, so a bitmap added during that window would never get stored either. Wrapped in a transaction, this denies the whole transaction, since qmp_transaction() is already all-or-none. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Message-ID: <20260716112242.3000035-2-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/monitor/bitmap-qmp-cmds.c | 15 +++++++--- qapi/block-core.json | 4 ++- .../tests/remove-bitmap-from-backing | 29 ++++++++++++++++++- .../tests/remove-bitmap-from-backing.out | 20 +++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index a738e7bbf7..d87ca982aa 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -125,10 +125,17 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, disabled = false; } - if (persistent && - !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) - { - return; + if (persistent) { + if (!bdrv_is_writable(bs)) { + error_setg(errp, "Cannot add a persistent bitmap to " + "read-only or inactive node '%s'", + bdrv_get_node_name(bs)); + return; + } + + if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { + return; + } } bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); diff --git a/qapi/block-core.json b/qapi/block-core.json index 1f87b07850..199efc1e00 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2353,7 +2353,9 @@ # @persistent: the bitmap is persistent, i.e. it will be saved to the # corresponding block device image file on its close. For now # only Qcow2 disks support persistent bitmaps. Default is false -# for `block-dirty-bitmap-add`. (Since: 2.10) +# for `block-dirty-bitmap-add`. This fails if the node is +# read-only or inactive, since such a bitmap could never be +# stored. (Since: 2.10) # # @disabled: the bitmap is created in the disabled state, which means # that it will not track drive changes. The bitmap may be enabled diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing b/tests/qemu-iotests/tests/remove-bitmap-from-backing index 15be32dcb9..a54984fa58 100755 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing @@ -35,7 +35,7 @@ qemu_img('bitmap', '--add', base, 'bitmap0') # Just assert that our method of checking bitmaps in the image works. assert 'bitmaps' in qemu_img_info(base)['format-specific']['data'] -vm = iotests.VM().add_drive(top, 'backing.node-name=base') +vm = iotests.VM().add_drive(top, 'node-name=top,backing.node-name=base') vm.launch() log('Trying to remove persistent bitmap from r-o base node, should fail:') @@ -66,6 +66,33 @@ result = vm.qmp('blockdev-reopen', **new_base_opts) if result != {'return': {}}: log('Failed to reopen: ' + str(result)) +log('Adding a persistent bitmap to the r-o base node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node='base', name='bitmap1', + persistent=True) + +log('Same add inside a transaction, preceded by an otherwise valid') +log('action: the whole transaction must fail and roll back the') +log('already-succeeded first action too:') +vm.qmp_log('transaction', actions=[ + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'top', 'name': 'bitmap2', 'persistent': True}}, + {'type': 'block-dirty-bitmap-add', + 'data': {'node': 'base', 'name': 'bitmap1', 'persistent': True}}, +]) + +log('bitmap2 on the rw top node must not have survived the rollback:') +vm.qmp_log('block-dirty-bitmap-remove', node='top', name='bitmap2') + +log('Marking the rw top node inactive:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': False}) + +log('Adding a persistent bitmap to a rw but inactive node, should fail:') +vm.qmp_log('block-dirty-bitmap-add', node='top', name='bitmap3', + persistent=True) + +log('Reactivating the top node:') +vm.qmp_log('blockdev-set-active', **{'node-name': 'top', 'active': True}) + vm.shutdown() if 'bitmaps' in qemu_img_info(base)['format-specific']['data']: diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out index c28af82c75..fe105fe0a3 100644 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out @@ -4,3 +4,23 @@ Trying to remove persistent bitmap from r-o base node, should fail: Remove persistent bitmap from base node reopened to RW: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "base"}} {"return": {}} +Adding a persistent bitmap to the r-o base node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap1", "node": "base", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to read-only or inactive node 'base'"}} +Same add inside a transaction, preceded by an otherwise valid +action: the whole transaction must fail and roll back the +already-succeeded first action too: +{"execute": "transaction", "arguments": {"actions": [{"data": {"name": "bitmap2", "node": "top", "persistent": true}, "type": "block-dirty-bitmap-add"}, {"data": {"name": "bitmap1", "node": "base", "persistent": true}, "type": "block-dirty-bitmap-add"}]}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to read-only or inactive node 'base'"}} +bitmap2 on the rw top node must not have survived the rollback: +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap2", "node": "top"}} +{"error": {"class": "GenericError", "desc": "Dirty bitmap 'bitmap2' not found"}} +Marking the rw top node inactive: +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "top"}} +{"return": {}} +Adding a persistent bitmap to a rw but inactive node, should fail: +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "bitmap3", "node": "top", "persistent": true}} +{"error": {"class": "GenericError", "desc": "Cannot add a persistent bitmap to read-only or inactive node 'top'"}} +Reactivating the top node: +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "top"}} +{"return": {}} From 3f3f5cdb929e8621699f92ee6e5018e1e6ea5bf2 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 16 Jul 2026 13:22:41 +0200 Subject: [PATCH 581/860] migration/block-dirty-bitmap: reject bitmap load onto ro node dirty_bitmap_load_start() creates an incoming migrated bitmap with bdrv_create_dirty_bitmap() and, if the source marked it persistent, calls bdrv_dirty_bitmap_set_persistence() without checking whether the destination node can be written to. Same gap as qmp_block_dirty_bitmap_add(), reached via incoming migration: a persistent bitmap for a read-only destination (e.g. a migrated CD-ROM-class attachment with dirty-bitmaps migration enabled) ends up writable in memory on a node that can never store it. Reject it the same way, with one difference from the QMP path: every destination node is BDRV_O_INACTIVE until migration completes, so bdrv_is_writable() would reject every incoming persistent bitmap, not just read-only ones. Check bdrv_is_read_only() alone. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Message-ID: <20260716112242.3000035-3-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- migration/block-dirty-bitmap.c | 22 +++++++++---- tests/qemu-iotests/tests/migrate-bitmaps-test | 33 +++++++++++++++++++ .../tests/migrate-bitmaps-test.out | 4 +-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index cba54e25cd..1b8f39c12b 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -812,13 +812,6 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s) error_report("Bitmap with the same name ('%s') already exists on " "destination", bdrv_dirty_bitmap_name(s->bitmap)); return -EINVAL; - } else { - s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity, - s->bitmap_name, &local_err); - if (!s->bitmap) { - error_report_err(local_err); - return -EINVAL; - } } if (flags & DIRTY_BITMAP_MIG_START_FLAG_RESERVED_MASK) { @@ -835,6 +828,21 @@ static int dirty_bitmap_load_start(QEMUFile *f, DBMLoadState *s) persistent = flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; } + /* Not bdrv_is_writable(): nodes stay inactive until migration ends. */ + if (persistent && bdrv_is_read_only(s->bs)) { + error_report("Cannot make migrated bitmap '%s' persistent " + "on read-only node '%s'", s->bitmap_name, + bdrv_get_node_name(s->bs)); + return -EINVAL; + } + + s->bitmap = bdrv_create_dirty_bitmap(s->bs, granularity, + s->bitmap_name, &local_err); + if (!s->bitmap) { + error_report_err(local_err); + return -EINVAL; + } + if (persistent) { bdrv_dirty_bitmap_set_persistence(s->bitmap, true); } diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test index 8fb4099201..cb9154ca8d 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -206,6 +206,39 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): self.vm_b.launch() self.check_bitmap(self.vm_b, sha256 if persistent else False) + def test_migration_to_readonly_destination(self): + granularity = 512 + mig_caps = [{'capability': 'events', 'state': True}, + {'capability': 'dirty-bitmaps', 'state': True}] + + self.vm_b.add_incoming("defer") + self.vm_b.add_drive(disk_b, 'read-only=on') + + self.add_bitmap(self.vm_a, granularity, True) + self.vm_a.hmp_qemu_io('drive0', 'write 0 4096') + + self.vm_a.cmd('migrate-set-capabilities', capabilities=mig_caps) + self.vm_a.cmd('migrate', uri=mig_cmd) + while True: + event = self.vm_a.event_wait('MIGRATION') + if event['data']['status'] == 'completed': + break + self.vm_a.shutdown() + + self.vm_b.launch() + self.vm_b.cmd('migrate-set-capabilities', capabilities=mig_caps) + self.vm_b.cmd('migrate-incoming', uri=incoming_cmd) + while True: + event = self.vm_b.event_wait('MIGRATION') + if event['data']['status'] in ('completed', 'failed'): + break + + self.assert_qmp(event, 'data/status', 'failed') + + # A failed incoming load makes the destination process exit on + # its own; reap it so tearDown()'s shutdown() is a clean no-op. + self.vm_b.wait() + def inject_test_case(klass, suffix, method, *args, **kwargs): mc = operator.methodcaller(method, *args, **kwargs) diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test.out b/tests/qemu-iotests/tests/migrate-bitmaps-test.out index cafb8161f7..73e375a9d7 100644 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test.out +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test.out @@ -1,5 +1,5 @@ -..................................... +...................................... ---------------------------------------------------------------------- -Ran 37 tests +Ran 38 tests OK From 9a77ef813f5f915bda408f4e53c11d0ca17c5d70 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 16 Jul 2026 13:22:42 +0200 Subject: [PATCH 582/860] block/monitor: allow dropping a bitmap never stored on disk block-dirty-bitmap-remove refuses any readonly bitmap outright, via the generic BDRV_BITMAP_RO check in bdrv_dirty_bitmap_check(). That check cannot tell whether the bitmap is actually on disk, so it also blocks dropping one that only ever existed in memory, which needs no write at all. Drop the blanket check and let qcow2 decide: bdrv_remove_persistent_ dirty_bitmap() already treats an absent on-disk entry as a no-op, so such a bitmap is now released with no write attempted. For one that is genuinely stored, qcow2_co_remove_persistent_dirty_bitmap_locked() now checks can_write() before it would update the on-disk directory, so removal still fails there, with a message naming the actual reason instead of just the bitmap's readonly flag. Signed-off-by: Denis V. Lunev CC: Eric Blake CC: Vladimir Sementsov-Ogievskiy CC: John Snow CC: Andrey Drobyshev Message-ID: <20260716112242.3000035-4-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/monitor/bitmap-qmp-cmds.c | 4 ++-- block/qcow2-bitmap.c | 9 +++++++++ tests/qemu-iotests/tests/remove-bitmap-from-backing.out | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/block/monitor/bitmap-qmp-cmds.c b/block/monitor/bitmap-qmp-cmds.c index d87ca982aa..aabf2790b6 100644 --- a/block/monitor/bitmap-qmp-cmds.c +++ b/block/monitor/bitmap-qmp-cmds.c @@ -165,11 +165,11 @@ BdrvDirtyBitmap *block_dirty_bitmap_remove(const char *node, const char *name, return NULL; } - if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, - errp)) { + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { return NULL; } + /* Dropping a bitmap needs no write access unless it is actually stored. */ if (bdrv_dirty_bitmap_get_persistence(bitmap) && bdrv_remove_persistent_dirty_bitmap(bs, name, errp) < 0) { diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 256ec99878..ac5a724588 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -1487,6 +1487,15 @@ int coroutine_fn qcow2_co_remove_persistent_dirty_bitmap(BlockDriverState *bs, goto out; } + if (!can_write(bs)) { + error_setg(errp, "Cannot remove persistent bitmap '%s': " + "no write access to node '%s'", name, + bdrv_get_node_name(bs)); + ret = -EACCES; + bm = NULL; + goto out; + } + QSIMPLEQ_REMOVE(bm_list, bm, Qcow2Bitmap, entry); ret = update_ext_header_and_dir(bs, bm_list); diff --git a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out index fe105fe0a3..628fa737d9 100644 --- a/tests/qemu-iotests/tests/remove-bitmap-from-backing.out +++ b/tests/qemu-iotests/tests/remove-bitmap-from-backing.out @@ -1,6 +1,6 @@ Trying to remove persistent bitmap from r-o base node, should fail: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "base"}} -{"error": {"class": "GenericError", "desc": "Bitmap 'bitmap0' is readonly and cannot be modified"}} +{"error": {"class": "GenericError", "desc": "Cannot remove persistent bitmap 'bitmap0': no write access to node 'base'"}} Remove persistent bitmap from base node reopened to RW: {"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmap0", "node": "base"}} {"return": {}} From dc04053687ec7219a78f9b90fe2f71c9336914e1 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 11 Aug 2026 19:38:52 +0200 Subject: [PATCH 583/860] dirty-bitmap: fix integer overflow in serialization coverage The chunk size is an int and is shifted left by 3 before the result is widened, so a chunk size of 1 << 28 or above overflows. parallels passes s->cluster_size, which parallels_open() lets reach 2 GiB. With a bitmap needing two L1 entries the bogus limit makes the "bm_size - offset" in parallels_load_bitmap_data() underflow; both wrong values slip past the assertions in serialization_chunk() and the resulting index lands outside the hbitmap, so a 128 KiB image memsets unrelated memory through hbitmap_deserialize_ones(). Widen the shift. qcow2, the only other caller, never exceeds a 2 MiB cluster. Fixes: 35f428ba3971 ("qcow2-bitmap: make bytes_covered_by_bitmap_cluster() public") Cc: Eric Blake Cc: Vladimir Sementsov-Ogievskiy Cc: Stefan Hajnoczi Cc: Thomas Huth Signed-off-by: Denis V. Lunev Message-ID: <20260811173857.396571-4-den@openvz.org> Reviewed-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/dirty-bitmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 13a1979755..9fda3a4b98 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -612,7 +612,7 @@ uint64_t bdrv_dirty_bitmap_serialization_coverage(int serialized_chunk_size, const BdrvDirtyBitmap *bitmap) { uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap); - uint64_t limit = granularity * (serialized_chunk_size << 3); + uint64_t limit = granularity * ((uint64_t)serialized_chunk_size << 3); assert(QEMU_IS_ALIGNED(limit, bdrv_dirty_bitmap_serialization_align(bitmap))); From 3181e53c13a1a68e2b54c94b1766f8e384c01b4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 15:51:33 -0700 Subject: [PATCH 584/860] tcg/optimize: INDEX_op_mul is commutative MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: qemu-stable@nongnu.org Fixes: 7a2f7084525 ("tcg/optimize: Sink commutative operand swapping into fold functions") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index fcdef25bee..ed2ff32ed2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2152,7 +2152,7 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) static bool fold_mul(OptContext *ctx, TCGOp *op) { - if (fold_const2(ctx, op) || + if (fold_const2_commutative(ctx, op) || fold_xi_to_i(ctx, op, 0) || fold_xi_to_x(ctx, op, 1)) { return true; From 5fe51606146cbf04dac07bf27256febbe56881e7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 14:15:00 -0700 Subject: [PATCH 585/860] tcg/optimize: Fix s_mask computation for shifts Skip s_mask computation for logical right shift. Cc: qemu-stable@nongnu.org Fixes: 93a967fbb57 ("tcg/optimize: Propagate sign info for shifting") Reported-by: Jacob Young Signed-off-by: Richard Henderson --- tcg/optimize.c | 11 ++++++++++- tests/tcg/i386/test-i386-opt-shr.c | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/tcg/i386/test-i386-opt-shr.c diff --git a/tcg/optimize.c b/tcg/optimize.c index ed2ff32ed2..d12babad88 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2656,8 +2656,17 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); o_mask = do_constant_folding(op->opc, ctx->type, o_mask, sh); - s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); + if (op->opc == INDEX_op_shr) { + /* + * Logical right shift will force the sign bit zero. + * Don't bother computing s_mask and let fold_masks + * recompute from z_mask. + */ + return fold_masks_zo(ctx, op, z_mask, o_mask); + } + + s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } diff --git a/tests/tcg/i386/test-i386-opt-shr.c b/tests/tcg/i386/test-i386-opt-shr.c new file mode 100644 index 0000000000..9fb8d42022 --- /dev/null +++ b/tests/tcg/i386/test-i386-opt-shr.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Regression test for tcg optimize vs sign bit repetition counting. */ + +#include + +int main() +{ +#ifndef __x86_64__ + char test; + + asm("movw $0x4000, %%ax\n\t" + "addw %%ax, %%ax\n\t" + "cwtl\n\t" + "shrl %%eax\n\t" + "cmpw $-0x3fff, %%ax\n\t" + "setnl %%al" + : "=a"(test)); + assert(!test); +#endif + return 0; +} From 784b7f28e2761fbd3c753883be949eb3d1ffe228 Mon Sep 17 00:00:00 2001 From: Ilya Chichkov Date: Fri, 14 Aug 2026 17:21:59 +0300 Subject: [PATCH 586/860] accel/tcg: Allow overlapping reads in record_save record_save() assumed that a target reads the bytes of an insn as a strictly ascending sequence of adjacent chunks, and asserted that each read begins exactly where the previous one ended. That assumption no longer holds for riscv. Since f9eaa1542b ("target/riscv: support atomic instruction fetch (Ziccif)"), decode_opc() loads a full aligned word whenever pc is 4-byte aligned, even when the insn turns out to be a 2-byte compressed one, so the record may already hold bytes past the end of the insn being translated. When such a compressed insn sits at page offset 0xffc, pc_next becomes 0xffe, which is within MAX_INSN_LEN of the end of the page, and riscv_tr_translate_insn() probes the next insn to decide whether it would cross the page boundary. That probe reads at offset 2 while the record already covers [0,4), and the assert fires: qemu-system-riscv32: accel/tcg/translator.c:395: record_save: Assertion `offset == db->record_start + db->record_len' failed. record_save() is only reached when the insn is fetched from MMIO, so this is visible on boards that execute code from a region created with memory_region_init_io(), such as an XIP flash window mapped over a serial flash controller. Both sides of the collision are correct: the wide fetch is required for Ziccif atomicity, and the probe is required for correct fault reporting at a page boundary, per 00c07344fa ("target/riscv: Make translator stop before the end of a page"). Unlike a86d3352ab ("target/riscv: do not use translator_ldl in opcode_at"), where a non-translation caller had no business using translator_ld*, the probe here is a genuine translation read whose bytes must be recorded. Relax the invariant instead. Keep requiring that a read neither moves backwards nor leaves a gap, but let a read overlapping the recorded range extend it only by the bytes past its end. Cc: qemu-stable@nongnu.org Fixes: f9eaa1542b ("target/riscv: support atomic instruction fetch (Ziccif)") Signed-off-by: Ilya Chichkov Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20260814142159.3800744-1-ilya.chichkov.dev@gmail.com> --- accel/tcg/translator.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index cd7d079fe0..57daded60f 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -387,14 +387,22 @@ static void record_save(DisasContextBase *db, vaddr pc, * Either the first or second page may be I/O. If it is the second, * then the first byte we need to record will be at a non-zero offset. * In either case, we should not need to record but a single insn. + * + * A read may re-read bytes that are already recorded: a target may + * fetch a whole aligned word to decode an insn (e.g. riscv Ziccif), + * then probe the following insn, which lies within that same word. + * Such a read extends the record only by the bytes past its end. */ if (db->record_len == 0) { db->record_start = offset; db->record_len = size; } else { - assert(offset == db->record_start + db->record_len); - assert(db->record_len + size <= sizeof(db->record)); - db->record_len += size; + int end = offset - db->record_start + size; + + assert(offset >= db->record_start); + assert(offset <= db->record_start + db->record_len); + assert(end <= sizeof(db->record)); + db->record_len = MAX(db->record_len, end); } memcpy(db->record + (offset - db->record_start), from, size); From e131c28b6eb24c0b7f7f21591b2b2c7568267337 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 16:06:10 -0700 Subject: [PATCH 587/860] tcg: Return success from tcg_region_alloc__locked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Invert the sense of the boolean result from 'error' to 'success'. Tested-by: Yogesh Vyas Reviewed-by: Yogesh Vyas Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/region.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tcg/region.c b/tcg/region.c index 5d4be1453b..2161d961d9 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -360,11 +360,11 @@ static void tcg_region_assign(TCGContext *s, size_t curr_region) static bool tcg_region_alloc__locked(TCGContext *s) { if (region.current == region.n) { - return true; + return false; } tcg_region_assign(s, region.current); region.current++; - return false; + return true; } /* @@ -373,17 +373,17 @@ static bool tcg_region_alloc__locked(TCGContext *s) */ bool tcg_region_alloc(TCGContext *s) { - bool err; + bool ok; /* read the region size now; alloc__locked will overwrite it on success */ size_t size_full = s->code_gen_buffer_size; qemu_mutex_lock(®ion.lock); - err = tcg_region_alloc__locked(s); - if (!err) { + ok = tcg_region_alloc__locked(s); + if (ok) { region.agg_size_full += size_full - TCG_HIGHWATER; } qemu_mutex_unlock(®ion.lock); - return err; + return !ok; } /* @@ -392,8 +392,8 @@ bool tcg_region_alloc(TCGContext *s) */ static void tcg_region_initial_alloc__locked(TCGContext *s) { - bool err = tcg_region_alloc__locked(s); - g_assert(!err); + bool ok = tcg_region_alloc__locked(s); + g_assert(ok); } void tcg_region_initial_alloc(TCGContext *s) From fcdbc78ad4dea4b6baac581be5f7350ce47dc218 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 16:08:40 -0700 Subject: [PATCH 588/860] tcg: Return success from tcg_region_alloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Invert the sense of the boolean result from 'error' to 'success'. Tested-by: Yogesh Vyas Reviewed-by: Yogesh Vyas Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/region.c | 4 ++-- tcg/tcg.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tcg/region.c b/tcg/region.c index 2161d961d9..57ca742357 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -369,7 +369,7 @@ static bool tcg_region_alloc__locked(TCGContext *s) /* * Request a new region once the one in use has filled up. - * Returns true on error. + * Returns true on success. */ bool tcg_region_alloc(TCGContext *s) { @@ -383,7 +383,7 @@ bool tcg_region_alloc(TCGContext *s) region.agg_size_full += size_full - TCG_HIGHWATER; } qemu_mutex_unlock(®ion.lock); - return !ok; + return ok; } /* diff --git a/tcg/tcg.c b/tcg/tcg.c index 1e77f2365a..af15c3d63e 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1835,7 +1835,7 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); if (unlikely(next > s->code_gen_highwater)) { - if (tcg_region_alloc(s)) { + if (!tcg_region_alloc(s)) { return NULL; } goto retry; From f5d2d8532f3c3cefb644fe9c1866a21b75f76faf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 17:11:28 -0700 Subject: [PATCH 589/860] tcg: Defer tb_flush when initial thread region alloc fails A vCPU hotplug may happen at any time. When the new thread is started, the region pool may be exhausted. Do not abort. Rename tcg_region_thread_initial_alloc to differentiate it from tcg_region_initial_alloc__locked. The renamed function now uses tcg_region_alloc__locked and is prepared for failure. In tcg_tb_alloc, allow code_gen_ptr to be NULL. Treat that as any other region exhaustion. Reorg with while instead of goto. Tested-by: Yogesh Vyas Reviewed-by: Yogesh Vyas Reported-by: Anushree Mathur Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2984 Signed-off-by: Richard Henderson --- tcg/region.c | 24 ++++++++++++++++++++++-- tcg/tcg-internal.h | 2 +- tcg/tcg.c | 22 ++++++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/tcg/region.c b/tcg/region.c index 57ca742357..1145f35085 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -396,11 +396,31 @@ static void tcg_region_initial_alloc__locked(TCGContext *s) g_assert(ok); } -void tcg_region_initial_alloc(TCGContext *s) +void tcg_region_thread_initial_alloc(TCGContext *s) { + bool ok; + qemu_mutex_lock(®ion.lock); - tcg_region_initial_alloc__locked(s); + ok = tcg_region_alloc__locked(s); qemu_mutex_unlock(®ion.lock); + + /* + * A vCPU hotplug may happen at any time. When the new thread is + * started, the region pool may be exhausted. At this point in + * the new thread call stack, we are not in a position to fix this. + * Leave code_gen_ptr NULL, so that this thread's first call to + * tcg_tb_alloc() returns NULL, so that the translator performs + * a tb_flush() and retry. + * + * During the tb_flush(), tcg_region_reset_all() will assign a + * new region to all contexts, including this one. + */ + if (!ok) { + s->code_gen_buffer = NULL; + s->code_gen_ptr = NULL; + s->code_gen_buffer_size = 0; + s->code_gen_highwater = NULL; + } } /* Call from a safe-work context */ diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index c0997ab224..e35440dc8c 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -42,7 +42,7 @@ extern unsigned int tcg_max_ctxs; void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads); bool tcg_region_alloc(TCGContext *s); -void tcg_region_initial_alloc(TCGContext *s); +void tcg_region_thread_initial_alloc(TCGContext *s); void tcg_region_prologue_set(TCGContext *s); static inline void *tcg_call_func(TCGOp *op) diff --git a/tcg/tcg.c b/tcg/tcg.c index af15c3d63e..db43589fa2 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1279,7 +1279,7 @@ void tcg_register_thread(void) qatomic_set(&tcg_ctxs[n], s); if (n > 0) { - tcg_region_initial_alloc(s); + tcg_region_thread_initial_alloc(s); } tcg_ctx = s; @@ -1830,18 +1830,24 @@ TranslationBlock *tcg_tb_alloc(TCGContext *s) TranslationBlock *tb; void *next; - retry: - tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + while (1) { + tb = (void *)ROUND_UP((uintptr_t)s->code_gen_ptr, align); - if (unlikely(next > s->code_gen_highwater)) { + /* + * Note that code_gen_ptr can be NULL after vCPU hotplug. + * See tcg_region_thread_initial_alloc. + */ + if (tb) { + next = (void *)ROUND_UP((uintptr_t)(tb + 1), align); + if (next <= s->code_gen_highwater) { + qatomic_set(&s->code_gen_ptr, next); + return tb; + } + } if (!tcg_region_alloc(s)) { return NULL; } - goto retry; } - qatomic_set(&s->code_gen_ptr, next); - return tb; } void tcg_prologue_init(void) From 3f3a1ea472d1fc25ecf70087e0a33e57c7f191f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 15:37:38 -0700 Subject: [PATCH 590/860] tcg: Fix opcode dump for bswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use an array of char for bswap_flag_name, so some entries in the array are non-null but empty. Check that. Fixes: 587195bd590 ("tcg: Add flags argument to bswap opcodes") Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index db43589fa2..937d0c8fd7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2957,7 +2957,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) if (flags < ARRAY_SIZE(bswap_flag_name)) { name = bswap_flag_name[flags]; } - if (name) { + if (name && name[0]) { col += ne_fprintf(f, ",%s", name); } else { col += ne_fprintf(f, ",$0x%" TCG_PRIlx, flags); From c2ca3737400ebf9432bf14fc59db69538868466d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 10:55:05 -0700 Subject: [PATCH 591/860] tcg: Add tcg_gen_revbit{8,32,64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add generic expanders for reversing bits within a word. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg-op-common.h | 5 +++ include/tcg/tcg-op.h | 7 ++++ tcg/tcg-op.c | 76 +++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 1fe342db0d..f7b6aadb50 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -164,6 +164,8 @@ void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); +void tcg_gen_revbit8_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg); /* Replicate a value of size @vece from @in to all the lanes in @out */ void tcg_gen_dup_i32(unsigned vece, TCGv_i32 out, TCGv_i32 in); @@ -275,6 +277,9 @@ void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); +void tcg_gen_revbit8_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_revbit32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); +void tcg_gen_revbit64_i64(TCGv_i64 ret, TCGv_i64 arg); /* Replicate a value of size @vece from @in to all the lanes in @out */ void tcg_gen_dup_i64(unsigned vece, TCGv_i64 out, TCGv_i64 in); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 96a5af1a29..568b78fe81 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -115,6 +115,10 @@ typedef TCGv_i64 TCGv; #define tcg_gen_bswap_tl tcg_gen_bswap64_i64 #define tcg_gen_hswap_tl tcg_gen_hswap_i64 #define tcg_gen_wswap_tl tcg_gen_wswap_i64 +#define tcg_gen_revbit8_tl tcg_gen_revbit8_i64 +#define tcg_gen_revbit32_tl tcg_gen_revbit32_i64 +#define tcg_gen_revbit64_tl tcg_gen_revbit64_i64 +#define tcg_gen_revbit_tl tcg_gen_revbit64_i64 #define tcg_gen_concat_tl_i64 tcg_gen_concat32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr32_i64 #define tcg_gen_andc_tl tcg_gen_andc_i64 @@ -234,6 +238,9 @@ typedef TCGv_i64 TCGv; #define tcg_gen_bswap32_tl(D, S, F) tcg_gen_bswap32_i32(D, S) #define tcg_gen_bswap_tl tcg_gen_bswap32_i32 #define tcg_gen_hswap_tl tcg_gen_hswap_i32 +#define tcg_gen_revbit8_tl tcg_gen_revbit8_i32 +#define tcg_gen_revbit32_tl(D, S, F) tcg_gen_revbit32_i32(D, S) +#define tcg_gen_revbit_tl tcg_gen_revbit32_i32 #define tcg_gen_concat_tl_i64 tcg_gen_concat_i32_i64 #define tcg_gen_extr_i64_tl tcg_gen_extr_i64_i32 #define tcg_gen_andc_tl tcg_gen_andc_i32 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index bbcb510c76..3d28280785 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1160,6 +1160,45 @@ void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) tcg_gen_extract_i32(ret, arg, 0, 16); } +/* + * Internal helper for bit and byte reversal. + * Given a repeating matched block of 1's and 0's, swap the bits within + * those two blocks. E.g. mask=00ff00ff, shift the input bits left and + * right 8 bits. + */ +static void gen_bitswap_i32(TCGv_i32 ret, TCGv_i32 arg, uint32_t mask) +{ + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + int sh = cto32(mask); + + tcg_gen_andi_i32(t0, arg, mask); + tcg_gen_shri_i32(t1, arg, sh); + tcg_gen_shli_i32(t0, t0, sh); + tcg_gen_andi_i32(t1, t1, mask); + tcg_gen_or_i32(ret, t0, t1); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); +} + +/* Similarly for 64-bit operands. */ +static void gen_bitswap_i64(TCGv_i64 ret, TCGv_i64 arg, uint64_t mask) +{ + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + int sh = cto64(mask); + + tcg_gen_andi_i64(t0, arg, mask); + tcg_gen_shri_i64(t1, arg, sh); + tcg_gen_shli_i64(t0, t0, sh); + tcg_gen_andi_i64(t1, t1, mask); + tcg_gen_or_i64(ret, t0, t1); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + /* * bswap16_i32: 16-bit byte swap on the low bits of a 32-bit value. * @@ -1244,6 +1283,19 @@ void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg) tcg_gen_rotli_i32(ret, arg, 16); } +void tcg_gen_revbit8_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + gen_bitswap_i32(ret, arg, 0x55555555u); + gen_bitswap_i32(ret, ret, 0x33333333u); + gen_bitswap_i32(ret, ret, 0x0f0f0f0fu); +} + +void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg) +{ + tcg_gen_revbit8_i32(ret, arg); + tcg_gen_bswap32_i32(ret, ret); +} + void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); @@ -1869,6 +1921,30 @@ void tcg_gen_wswap_i64(TCGv_i64 ret, TCGv_i64 arg) tcg_gen_rotli_i64(ret, arg, 32); } +void tcg_gen_revbit32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) +{ + /* Only one extension flag may be present. */ + tcg_debug_assert(!(flags & TCG_BSWAP_OS) || !(flags & TCG_BSWAP_OZ)); + + gen_bitswap_i64(ret, arg, 0x55555555ull); + gen_bitswap_i64(ret, ret, 0x33333333ull); + gen_bitswap_i64(ret, ret, 0x0f0f0f0full); + tcg_gen_bswap32_i64(ret, ret, flags | TCG_BSWAP_IZ); +} + +void tcg_gen_revbit8_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + gen_bitswap_i64(ret, arg, 0x5555555555555555ull); + gen_bitswap_i64(ret, ret, 0x3333333333333333ull); + gen_bitswap_i64(ret, ret, 0x0f0f0f0f0f0f0f0full); +} + +void tcg_gen_revbit64_i64(TCGv_i64 ret, TCGv_i64 arg) +{ + tcg_gen_revbit8_i64(ret, arg); + tcg_gen_bswap64_i64(ret, ret); +} + void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) { if (tcg_op_supported(INDEX_op_not, TCG_TYPE_I64, 0)) { From 288d39e26d48ed26e4f786ddb3a25eef0bca9f56 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Aug 2026 09:08:31 -0700 Subject: [PATCH 592/860] tcg: Simplify bswap/hswap expansion using bitswap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 72 ++++++---------------------------------------------- 1 file changed, 8 insertions(+), 64 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 3d28280785..c24a7962fc 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1252,23 +1252,8 @@ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) if (tcg_op_supported(INDEX_op_bswap32, TCG_TYPE_I32, 0)) { tcg_gen_op3i_i32(INDEX_op_bswap32, ret, arg, 0); } else { - TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - TCGv_i32 t1 = tcg_temp_ebb_new_i32(); - TCGv_i32 t2 = tcg_constant_i32(0x00ff00ff); - - /* arg = abcd */ - tcg_gen_shri_i32(t0, arg, 8); /* t0 = .abc */ - tcg_gen_and_i32(t1, arg, t2); /* t1 = .b.d */ - tcg_gen_and_i32(t0, t0, t2); /* t0 = .a.c */ - tcg_gen_shli_i32(t1, t1, 8); /* t1 = b.d. */ - tcg_gen_or_i32(ret, t0, t1); /* ret = badc */ - - tcg_gen_shri_i32(t0, ret, 16); /* t0 = ..ba */ - tcg_gen_shli_i32(t1, ret, 16); /* t1 = dc.. */ - tcg_gen_or_i32(ret, t0, t1); /* ret = dcba */ - - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); + gen_bitswap_i32(ret, arg, 0x00ff00ff); + tcg_gen_hswap_i32(ret, ret); } } @@ -1823,14 +1808,9 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - TCGv_i64 t2 = tcg_constant_i64(0x00ff00ff); - /* arg = xxxxabcd */ - tcg_gen_shri_i64(t0, arg, 8); /* t0 = .xxxxabc */ - tcg_gen_and_i64(t1, arg, t2); /* t1 = .....b.d */ - tcg_gen_and_i64(t0, t0, t2); /* t0 = .....a.c */ - tcg_gen_shli_i64(t1, t1, 8); /* t1 = ....b.d. */ - tcg_gen_or_i64(ret, t0, t1); /* ret = ....badc */ + /* arg = xxxxabcd */ + gen_bitswap_i64(ret, arg, 0x00ff00ff); /* ret = ....badc */ tcg_gen_shli_i64(t1, ret, 48); /* t1 = dc...... */ tcg_gen_shri_i64(t0, ret, 16); /* t0 = ......ba */ @@ -1857,32 +1837,8 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) if (tcg_op_supported(INDEX_op_bswap64, TCG_TYPE_I64, 0)) { tcg_gen_op3i_i64(INDEX_op_bswap64, ret, arg, 0); } else { - TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - TCGv_i64 t2 = tcg_temp_ebb_new_i64(); - - /* arg = abcdefgh */ - tcg_gen_movi_i64(t2, 0x00ff00ff00ff00ffull); - tcg_gen_shri_i64(t0, arg, 8); /* t0 = .abcdefg */ - tcg_gen_and_i64(t1, arg, t2); /* t1 = .b.d.f.h */ - tcg_gen_and_i64(t0, t0, t2); /* t0 = .a.c.e.g */ - tcg_gen_shli_i64(t1, t1, 8); /* t1 = b.d.f.h. */ - tcg_gen_or_i64(ret, t0, t1); /* ret = badcfehg */ - - tcg_gen_movi_i64(t2, 0x0000ffff0000ffffull); - tcg_gen_shri_i64(t0, ret, 16); /* t0 = ..badcfe */ - tcg_gen_and_i64(t1, ret, t2); /* t1 = ..dc..hg */ - tcg_gen_and_i64(t0, t0, t2); /* t0 = ..ba..fe */ - tcg_gen_shli_i64(t1, t1, 16); /* t1 = dc..hg.. */ - tcg_gen_or_i64(ret, t0, t1); /* ret = dcbahgfe */ - - tcg_gen_shri_i64(t0, ret, 32); /* t0 = ....dcba */ - tcg_gen_shli_i64(t1, ret, 32); /* t1 = hgfe.... */ - tcg_gen_or_i64(ret, t0, t1); /* ret = hgfedcba */ - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); - tcg_temp_free_i64(t2); + gen_bitswap_i64(ret, arg, 0x00ff00ff00ff00ffull); + tcg_gen_hswap_i64(ret, ret); } } @@ -1894,20 +1850,8 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) */ void tcg_gen_hswap_i64(TCGv_i64 ret, TCGv_i64 arg) { - uint64_t m = 0x0000ffff0000ffffull; - TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - - /* arg = abcdefgh */ - tcg_gen_rotli_i64(t1, arg, 32); /* t1 = efghabcd */ - tcg_gen_andi_i64(t0, t1, m); /* t0 = ..gh..cd */ - tcg_gen_shli_i64(t0, t0, 16); /* t0 = gh..cd.. */ - tcg_gen_shri_i64(t1, t1, 16); /* t1 = ..efghab */ - tcg_gen_andi_i64(t1, t1, m); /* t1 = ..ef..ab */ - tcg_gen_or_i64(ret, t0, t1); /* ret = ghefcdab */ - - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + gen_bitswap_i64(ret, ret, 0x0000ffff0000ffffull); + tcg_gen_wswap_i64(ret, ret); } /* From ab86f82ba9f190d3e9a46f6f354333f0b29ee905 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 11:09:58 -0700 Subject: [PATCH 593/860] target/arm: Use generic tcg_gen_revbit* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/arm/tcg/helper-a64-defs.h | 1 - target/arm/tcg/helper-a64.c | 5 ----- target/arm/tcg/helper-defs.h | 1 - target/arm/tcg/op_helper.c | 5 ----- target/arm/tcg/translate-a64.c | 4 ++-- target/arm/tcg/translate.c | 2 +- 6 files changed, 3 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/helper-a64-defs.h b/target/arm/tcg/helper-a64-defs.h index 0e56e00f45..12f58c6a44 100644 --- a/target/arm/tcg/helper-a64-defs.h +++ b/target/arm/tcg/helper-a64-defs.h @@ -18,7 +18,6 @@ */ DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) -DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(msr_i_spsel, void, env, i32) DEF_HELPER_2(msr_i_daifset, void, env, i32) DEF_HELPER_2(msr_i_daifclear, void, env, i32) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 05ab9ab6d3..9d805231a0 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -69,11 +69,6 @@ int64_t HELPER(sdiv64)(int64_t num, int64_t den) return num / den; } -uint64_t HELPER(rbit64)(uint64_t x) -{ - return revbit64(x); -} - void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm) { update_spsel(env, imm); diff --git a/target/arm/tcg/helper-defs.h b/target/arm/tcg/helper-defs.h index 0077aeb4e2..42376af2c6 100644 --- a/target/arm/tcg/helper-defs.h +++ b/target/arm/tcg/helper-defs.h @@ -10,7 +10,6 @@ DEF_HELPER_3(add_usaturate, i32, env, i32, i32) DEF_HELPER_3(sub_usaturate, i32, env, i32, i32) DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_RWG, s32, env, s32, s32) DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32) #define PAS_OP(pfx) \ DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index c4433be2ed..857e897a48 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -172,11 +172,6 @@ uint32_t HELPER(udiv)(CPUARMState *env, uint32_t num, uint32_t den) return num / den; } -uint32_t HELPER(rbit)(uint32_t x) -{ - return revbit32(x); -} - uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 1780490065..4f9a93950b 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8963,7 +8963,7 @@ static void gen_wrap2_i32(TCGv_i64 d, TCGv_i64 n, NeonGenOneOpFn fn) static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { - gen_wrap2_i32(tcg_rd, tcg_rn, gen_helper_rbit); + tcg_gen_revbit32_i64(tcg_rd, tcg_rn, TCG_BSWAP_OZ); } static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask) @@ -8998,7 +8998,7 @@ static void gen_rev32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) tcg_gen_rotri_i64(tcg_rd, tcg_rd, 32); } -TRANS(RBIT, gen_rr, a->rd, a->rn, a->sf ? gen_helper_rbit64 : gen_rbit32) +TRANS(RBIT, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_revbit64_i64 : gen_rbit32) TRANS(REV16, gen_rr, a->rd, a->rn, a->sf ? gen_rev16_64 : gen_rev16_32) TRANS(REV32, gen_rr, a->rd, a->rn, a->sf ? gen_rev32 : gen_rev_32) TRANS(REV64, gen_rr, a->rd, a->rn, tcg_gen_bswap64_i64) diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 055f3c5b40..1770428d3c 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -4835,7 +4835,7 @@ static bool trans_RBIT(DisasContext *s, arg_rr *a) if (!ENABLE_ARCH_6T2) { return false; } - return op_rr(s, a, gen_helper_rbit); + return op_rr(s, a, tcg_gen_revbit32_i32); } /* From 8c7ba1cedffb55827165d47f9216b24dccbd34e3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 11:29:30 -0700 Subject: [PATCH 594/860] target/loongarch: Use generic tcg_gen_revbit* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Song Gao <17746591750@163.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/loongarch/tcg/helper.h | 4 ---- .../loongarch/tcg/insn_trans/trans_bit.c.inc | 13 ++++++++---- target/loongarch/tcg/op_helper.c | 21 ------------------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h index 8a6c62f116..e76c73c775 100644 --- a/target/loongarch/tcg/helper.h +++ b/target/loongarch/tcg/helper.h @@ -5,10 +5,6 @@ DEF_HELPER_2(raise_exception, noreturn, env, i32) -DEF_HELPER_FLAGS_1(bitrev_w, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitrev_d, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) - DEF_HELPER_FLAGS_3(asrtle_d, TCG_CALL_NO_WG, void, env, tl, tl) DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl) diff --git a/target/loongarch/tcg/insn_trans/trans_bit.c.inc b/target/loongarch/tcg/insn_trans/trans_bit.c.inc index ee5fa003ce..b91bb7eaa6 100644 --- a/target/loongarch/tcg/insn_trans/trans_bit.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_bit.c.inc @@ -178,6 +178,11 @@ static void gen_masknez(TCGv dest, TCGv src1, TCGv src2) tcg_gen_movcond_tl(TCG_COND_NE, dest, src2, zero, zero, src1); } +static void gen_bitrev_w(TCGv dest, TCGv src) +{ + tcg_gen_revbit32_tl(dest, src, TCG_BSWAP_OS); +} + TRANS(ext_w_h, ALL, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext16s_tl) TRANS(ext_w_b, ALL, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_ext8s_tl) TRANS(clo_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_clo_w) @@ -194,10 +199,10 @@ TRANS(revb_2w, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revb_2w) TRANS(revb_d, 64, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_bswap64_i64) TRANS(revh_2w, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revh_2w) TRANS(revh_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_revh_d) -TRANS(bitrev_4b, ALL, gen_rr, EXT_ZERO, EXT_SIGN, gen_helper_bitswap) -TRANS(bitrev_8b, 64, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitswap) -TRANS(bitrev_w, ALL, gen_rr, EXT_NONE, EXT_SIGN, gen_helper_bitrev_w) -TRANS(bitrev_d, 64, gen_rr, EXT_NONE, EXT_NONE, gen_helper_bitrev_d) +TRANS(bitrev_4b, ALL, gen_rr, EXT_NONE, EXT_SIGN, tcg_gen_revbit8_i64) +TRANS(bitrev_8b, 64, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_revbit8_i64) +TRANS(bitrev_w, ALL, gen_rr, EXT_NONE, EXT_NONE, gen_bitrev_w) +TRANS(bitrev_d, 64, gen_rr, EXT_NONE, EXT_NONE, tcg_gen_revbit64_i64) TRANS(maskeqz, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_maskeqz) TRANS(masknez, ALL, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_masknez) TRANS(bytepick_w, ALL, gen_rrr_sa, EXT_NONE, EXT_NONE, gen_bytepick_w) diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c index e63ac66daa..f41f0cb1e6 100644 --- a/target/loongarch/tcg/op_helper.c +++ b/target/loongarch/tcg/op_helper.c @@ -22,27 +22,6 @@ void helper_raise_exception(CPULoongArchState *env, uint32_t exception) do_raise_exception(env, exception, GETPC()); } -target_ulong helper_bitrev_w(target_ulong rj) -{ - return (int32_t)revbit32(rj); -} - -target_ulong helper_bitrev_d(target_ulong rj) -{ - return revbit64(rj); -} - -target_ulong helper_bitswap(target_ulong v) -{ - v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | - ((v & (target_ulong)0x5555555555555555ULL) << 1); - v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | - ((v & (target_ulong)0x3333333333333333ULL) << 2); - v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | - ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); - return v; -} - /* loongarch assert op */ void helper_asrtle_d(CPULoongArchState *env, target_ulong rj, target_ulong rk) { From 8b416699c88f78c56eb668b18d40876034857542 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 12:04:53 -0700 Subject: [PATCH 595/860] target/mips: Expand octeon reflections inline Use tcg_gen_revbit64_i64 instead of out-of-line helpers. Reviewed-by: Anton Johansson Signed-off-by: Richard Henderson --- target/mips/helper.h | 7 ----- target/mips/tcg/octeon_crypto.c | 35 --------------------- target/mips/tcg/octeon_translate.c | 49 ++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 48 deletions(-) diff --git a/target/mips/helper.h b/target/mips/helper.h index 786117813a..779b87101d 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -27,10 +27,6 @@ DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32) /* Octeon COP2 selector operation helpers. */ DEF_HELPER_1(octeon_cp2_mf_crc_iv_reflect, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect0, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_mul_reflect1, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect0, i64, env) -DEF_HELPER_1(octeon_cp2_mf_gfm_resinp_reflect1, i64, env) DEF_HELPER_2(octeon_cp2_mt_crc_write_iv_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_polynomial_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_byte, void, env, i64) @@ -43,9 +39,6 @@ DEF_HELPER_2(octeon_cp2_mt_crc_write_dword, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_var, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_dword_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_crc_write_var_reflect, void, env, i64) -DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect0, void, env, i64) -DEF_HELPER_2(octeon_cp2_mt_gfm_mul_reflect1, void, env, i64) -DEF_HELPER_2(octeon_cp2_mt_gfm_xor0_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1_reflect, void, env, i64) DEF_HELPER_2(octeon_cp2_mt_gfm_xormul1, void, env, i64) DEF_HELPER_1(octeon_cp2_mt_sha3_startop, void, env) diff --git a/target/mips/tcg/octeon_crypto.c b/target/mips/tcg/octeon_crypto.c index fbf80be2a5..118397e632 100644 --- a/target/mips/tcg/octeon_crypto.c +++ b/target/mips/tcg/octeon_crypto.c @@ -2127,41 +2127,6 @@ uint64_t helper_octeon_cp2_mf_crc_iv_reflect(CPUMIPSState *env) return octeon_crc_reflect32_by_byte(env->octeon_crypto.crc_iv); } -uint64_t helper_octeon_cp2_mf_gfm_mul_reflect0(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_mul[0]); -} - -uint64_t helper_octeon_cp2_mf_gfm_mul_reflect1(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_mul[1]); -} - -uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect0(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_resinp[0]); -} - -uint64_t helper_octeon_cp2_mf_gfm_resinp_reflect1(CPUMIPSState *env) -{ - return revbit64(env->octeon_crypto.gfm_resinp[1]); -} - -void helper_octeon_cp2_mt_gfm_mul_reflect0(CPUMIPSState *env, uint64_t value) -{ - env->octeon_crypto.gfm_mul[0] = revbit64(value); -} - -void helper_octeon_cp2_mt_gfm_mul_reflect1(CPUMIPSState *env, uint64_t value) -{ - env->octeon_crypto.gfm_mul[1] = revbit64(value); -} - -void helper_octeon_cp2_mt_gfm_xor0_reflect(CPUMIPSState *env, uint64_t value) -{ - env->octeon_crypto.gfm_resinp[0] ^= revbit64(value); -} - static void octeon_gfm_xormul1_common(MIPSOcteonCryptoState *crypto, uint64_t value) { diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c index a0db6630c7..b689adb46b 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -28,6 +28,8 @@ TRANS(NAME, trans_octeon_cp2_mf_hsh_pair, \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX)]), \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX) + 1])) +#define CP2_MF_REFLECT(NAME, FIELD) \ + TRANS(NAME, trans_octeon_cp2_mf_reflect, OCTEON_CRYPTO_OFFSET(FIELD)) #define CP2_MF_HELPER(NAME, SUFFIX) \ TRANS(NAME, trans_octeon_cp2_mf_helper, \ gen_helper_octeon_cp2_mf_ ## SUFFIX) @@ -44,6 +46,8 @@ TRANS(NAME, trans_octeon_cp2_mt_hsh_pair, \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX)]), \ OCTEON_CRYPTO_OFFSET(FIELD[2 * (INDEX) + 1])) +#define CP2_MT_REFLECT(NAME, FIELD) \ + TRANS(NAME, trans_octeon_cp2_mt_reflect, OCTEON_CRYPTO_OFFSET(FIELD)) #define CP2_MT_HELPER(NAME, SUFFIX) \ TRANS(NAME, trans_octeon_cp2_mt_helper, \ gen_helper_octeon_cp2_mt_ ## SUFFIX) @@ -110,6 +114,17 @@ static bool trans_octeon_cp2_mf_hsh_pair(DisasContext *ctx, arg_cp2 *a, return true; } +static bool trans_octeon_cp2_mf_reflect(DisasContext *ctx, arg_cp2 *a, + int offset) +{ + TCGv_i64 value = tcg_temp_new_i64(); + + tcg_gen_ld_i64(value, tcg_env, offset); + tcg_gen_revbit64_i64(value, value); + gen_store_gpr(value, a->rt); + return true; +} + static bool trans_octeon_cp2_mf_helper(DisasContext *ctx, arg_cp2 *a, void (*gen_helper)(TCGv_i64, TCGv_env)) { @@ -183,6 +198,17 @@ static bool trans_octeon_cp2_mt_xor_i64(DisasContext *ctx, arg_cp2 *a, return true; } +static bool trans_octeon_cp2_mt_reflect(DisasContext *ctx, arg_cp2 *a, + int offset) +{ + TCGv_i64 value = tcg_temp_new_i64(); + + gen_load_gpr(value, a->rt); + tcg_gen_revbit64_i64(value, value); + tcg_gen_st_i64(value, tcg_env, offset); + return true; +} + static bool trans_octeon_cp2_mt_helper(DisasContext *ctx, arg_cp2 *a, void (*gen_helper)(TCGv_env, TCGv_i64)) { @@ -200,6 +226,17 @@ static bool trans_octeon_cp2_mt_helper_env(DisasContext *ctx, arg_cp2 *a, return true; } +static void gen_helper_octeon_cp2_mt_gfm_xor0_reflect(TCGv_env t_env, + TCGv_i64 value) +{ + TCGv_i64 resinp = tcg_temp_new_i64(); + + tcg_gen_revbit64_i64(value, value); + tcg_gen_ld_i64(resinp, t_env, OCTEON_CRYPTO_OFFSET(gfm_resinp[0])); + tcg_gen_xor_i64(resinp, resinp, value); + tcg_gen_st_i64(resinp, t_env, OCTEON_CRYPTO_OFFSET(gfm_resinp[0])); +} + CP2_MF_HSH_PAIR(CVM_MF_HSH_DAT0, hsh_dat, 0); CP2_MF_HSH_PAIR(CVM_MF_HSH_DAT1, hsh_dat, 1); CP2_MF_HSH_PAIR(CVM_MF_HSH_DAT2, hsh_dat, 2); @@ -241,10 +278,10 @@ CP2_MF_I64(CVM_MF_LLM_DATA1, llm_data[1]); CP2_MF_HELPER(CVM_MF_CRC_IV_REFLECT, crc_iv_reflect); CP2_MF_I64(CVM_MF_SHA3_DAT24, sha3_dat24); -CP2_MF_HELPER(CVM_MF_GFM_MUL_REFLECT0, gfm_mul_reflect0); -CP2_MF_HELPER(CVM_MF_GFM_MUL_REFLECT1, gfm_mul_reflect1); -CP2_MF_HELPER(CVM_MF_GFM_RESINP_REFLECT0, gfm_resinp_reflect0); -CP2_MF_HELPER(CVM_MF_GFM_RESINP_REFLECT1, gfm_resinp_reflect1); +CP2_MF_REFLECT(CVM_MF_GFM_MUL_REFLECT0, gfm_mul[0]) +CP2_MF_REFLECT(CVM_MF_GFM_MUL_REFLECT1, gfm_mul[1]) +CP2_MF_REFLECT(CVM_MF_GFM_RESINP_REFLECT0, gfm_resinp[0]) +CP2_MF_REFLECT(CVM_MF_GFM_RESINP_REFLECT1, gfm_resinp[1]) CP2_MF_I64(CVM_MF_HSH_DATW0, hsh_dat[0]); CP2_MF_I64(CVM_MF_HSH_DATW1, hsh_dat[1]); CP2_MF_I64(CVM_MF_HSH_DATW2, hsh_dat[2]); @@ -281,8 +318,8 @@ CP2_MT_HSH_PAIR(CVM_MT_HSH_IV0, hsh_iv, 0); CP2_MT_HSH_PAIR(CVM_MT_HSH_IV1, hsh_iv, 1); CP2_MT_HSH_PAIR(CVM_MT_HSH_IV2, hsh_iv, 2); CP2_MT_HSH_PAIR(CVM_MT_HSH_IV3, hsh_iv, 3); -CP2_MT_HELPER(CVM_MT_GFM_MUL_REFLECT0, gfm_mul_reflect0); -CP2_MT_HELPER(CVM_MT_GFM_MUL_REFLECT1, gfm_mul_reflect1); +CP2_MT_REFLECT(CVM_MT_GFM_MUL_REFLECT0, gfm_mul[0]); +CP2_MT_REFLECT(CVM_MT_GFM_MUL_REFLECT1, gfm_mul[1]); CP2_MT_HELPER(CVM_MT_GFM_XOR0_REFLECT, gfm_xor0_reflect); CP2_MT_I64(CVM_MT_3DES_KEY0, des3_key[0]); CP2_MT_I64(CVM_MT_3DES_KEY1, des3_key[1]); From 0a897b9c0c7e745a32ebfa363ebe9cc0218fb6cc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Aug 2026 09:42:10 -0700 Subject: [PATCH 596/860] target/riscv: Use generic tcg_gen_revbit8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/riscv/helper.h | 1 - target/riscv/tcg/bitmanip_helper.c | 15 --------------- target/riscv/tcg/insn_trans/trans_rvb.c.inc | 2 +- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 542b7c264f..4fc2d3a155 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -79,7 +79,6 @@ DEF_HELPER_FLAGS_2(froundnx_d, TCG_CALL_NO_RWG_SE, i64, env, i64) /* Bitmanip */ DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl) DEF_HELPER_FLAGS_2(clmulr, TCG_CALL_NO_RWG_SE, tl, tl, tl) -DEF_HELPER_FLAGS_1(brev8, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(unzip, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(zip, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_2(xperm4, TCG_CALL_NO_RWG_SE, tl, tl, tl) diff --git a/target/riscv/tcg/bitmanip_helper.c b/target/riscv/tcg/bitmanip_helper.c index 1156a87dd3..d8d94bca7b 100644 --- a/target/riscv/tcg/bitmanip_helper.c +++ b/target/riscv/tcg/bitmanip_helper.c @@ -52,21 +52,6 @@ target_ulong HELPER(clmulr)(target_ulong rs1, target_ulong rs2) return result; } -static inline target_ulong do_swap(target_ulong x, uint64_t mask, int shift) -{ - return ((x & mask) << shift) | ((x & ~mask) >> shift); -} - -target_ulong HELPER(brev8)(target_ulong rs1) -{ - target_ulong x = rs1; - - x = do_swap(x, 0x5555555555555555ull, 1); - x = do_swap(x, 0x3333333333333333ull, 2); - x = do_swap(x, 0x0f0f0f0f0f0f0f0full, 4); - return x; -} - static const uint64_t shuf_masks[] = { dup_const(MO_8, 0x44), dup_const(MO_8, 0x30), diff --git a/target/riscv/tcg/insn_trans/trans_rvb.c.inc b/target/riscv/tcg/insn_trans/trans_rvb.c.inc index e4dcc7c991..3e74be223b 100644 --- a/target/riscv/tcg/insn_trans/trans_rvb.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvb.c.inc @@ -522,7 +522,7 @@ static void gen_packw(TCGv ret, TCGv src1, TCGv src2) static bool trans_brev8(DisasContext *ctx, arg_brev8 *a) { REQUIRE_ZBKB(ctx); - return gen_unary(ctx, a, EXT_NONE, gen_helper_brev8); + return gen_unary(ctx, a, EXT_NONE, tcg_gen_revbit8_tl); } static bool trans_pack(DisasContext *ctx, arg_pack *a) From db1c58d777088b4c0de124c7b4d21d7b7d3adbab Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 15:35:50 -0700 Subject: [PATCH 597/860] tcg: Add revbit{8,32,64} opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the plumbing, but not yet implemented for any host. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 16 ++++++++ include/tcg/tcg-opc.h | 3 ++ tcg/aarch64/tcg-target.c.inc | 12 ++++++ tcg/loongarch64/tcg-target.c.inc | 12 ++++++ tcg/ppc64/tcg-target.c.inc | 12 ++++++ tcg/riscv64/tcg-target.c.inc | 12 ++++++ tcg/s390x/tcg-target.c.inc | 12 ++++++ tcg/sparc64/tcg-target.c.inc | 12 ++++++ tcg/tcg-op.c | 66 +++++++++++++++++++++++++------- tcg/tcg.c | 7 ++++ tcg/tci/tcg-target.c.inc | 12 ++++++ tcg/x86_64/tcg-target.c.inc | 12 ++++++ 12 files changed, 174 insertions(+), 14 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 92ef127c80..f2e9255dd9 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -495,6 +495,22 @@ Misc into 32-bit output *t0*. Depending on the host, this may be a simple shift, or may require additional canonicalization. + * - revbit8 *dest*, *t1* + + - | Reverse the 8 bits within each byte of input *t1* with + | output in *dest*; the byte order is unchanged. + + * - revbit32 *dest*, *t1*, *flags* + + - | Reverse the 32 bits of the lower 32 bits of input *t1* + | with output in *dest*. On TCG_TYPE_I64, *flags* control + | any required sign or zero extension of the result in + | the same way as for bswap32. + | On TCG_TYPE_I32, *flags* should be zero. + + * - revbit64 *dest*, *t1* + + - | Reverse the 64 bits of input *t1* with output in *dest*. Conditional moves ----------------- diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 61f1c28858..13c7f17f76 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -79,6 +79,9 @@ DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(rems, 1, 2, 0, TCG_OPF_INT) DEF(remu, 1, 2, 0, TCG_OPF_INT) +DEF(revbit8, 1, 1, 0, TCG_OPF_INT) +DEF(revbit32, 1, 1, 1, TCG_OPF_INT) +DEF(revbit64, 1, 1, 0, TCG_OPF_INT) DEF(rotl, 1, 2, 0, TCG_OPF_INT) DEF(rotr, 1, 2, 0, TCG_OPF_INT) DEF(sar, 1, 2, 0, TCG_OPF_INT) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index cc9c2a5158..0afa988087 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2652,6 +2652,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_XZR, a1); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 182dcfd5eb..97ed51d99c 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1866,6 +1866,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); diff --git a/tcg/ppc64/tcg-target.c.inc b/tcg/ppc64/tcg-target.c.inc index b54afa0b6d..07dff67e84 100644 --- a/tcg/ppc64/tcg-target.c.inc +++ b/tcg/ppc64/tcg-target.c.inc @@ -3421,6 +3421,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out32(s, NEG | RT(a0) | RA(a1)); diff --git a/tcg/riscv64/tcg-target.c.inc b/tcg/riscv64/tcg-target.c.inc index 76dd4fca97..687146e0b0 100644 --- a/tcg/riscv64/tcg-target.c.inc +++ b/tcg/riscv64/tcg-target.c.inc @@ -2469,6 +2469,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 84a9e73a46..c481745c3f 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3020,6 +3020,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { if (type == TCG_TYPE_I32) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 5e5c3f1cda..d6ed9d3362 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1947,6 +1947,18 @@ static const TCGOutOpUnary outop_bswap64 = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_G0, a1); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index c24a7962fc..c302a484cd 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1270,15 +1270,26 @@ void tcg_gen_hswap_i32(TCGv_i32 ret, TCGv_i32 arg) void tcg_gen_revbit8_i32(TCGv_i32 ret, TCGv_i32 arg) { - gen_bitswap_i32(ret, arg, 0x55555555u); - gen_bitswap_i32(ret, ret, 0x33333333u); - gen_bitswap_i32(ret, ret, 0x0f0f0f0fu); + if (tcg_op_supported(INDEX_op_revbit8, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_revbit8, ret, arg); + } else if (tcg_op_supported(INDEX_op_revbit32, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_revbit32, ret, arg); + tcg_gen_bswap32_i32(ret, ret); + } else { + gen_bitswap_i32(ret, arg, 0x55555555u); + gen_bitswap_i32(ret, ret, 0x33333333u); + gen_bitswap_i32(ret, ret, 0x0f0f0f0fu); + } } void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg) { - tcg_gen_revbit8_i32(ret, arg); - tcg_gen_bswap32_i32(ret, ret); + if (tcg_op_supported(INDEX_op_revbit32, TCG_TYPE_I32, 0)) { + tcg_gen_op3i_i32(INDEX_op_revbit32, ret, arg, 0); + } else { + tcg_gen_revbit8_i32(ret, arg); + tcg_gen_bswap32_i32(ret, ret); + } } void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) @@ -1870,23 +1881,50 @@ void tcg_gen_revbit32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) /* Only one extension flag may be present. */ tcg_debug_assert(!(flags & TCG_BSWAP_OS) || !(flags & TCG_BSWAP_OZ)); - gen_bitswap_i64(ret, arg, 0x55555555ull); - gen_bitswap_i64(ret, ret, 0x33333333ull); - gen_bitswap_i64(ret, ret, 0x0f0f0f0full); - tcg_gen_bswap32_i64(ret, ret, flags | TCG_BSWAP_IZ); + if (tcg_op_supported(INDEX_op_revbit32, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_revbit32, ret, arg, flags); + } else if (tcg_op_supported(INDEX_op_revbit64, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit64, ret, arg); + if (flags & TCG_BSWAP_OS) { + tcg_gen_sari_i64(ret, ret, 32); + } else { + tcg_gen_shri_i64(ret, ret, 32); + } + } else { + if (tcg_op_supported(INDEX_op_revbit8, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit8, ret, arg); + } else { + gen_bitswap_i64(ret, arg, 0x55555555ull); + gen_bitswap_i64(ret, ret, 0x33333333ull); + gen_bitswap_i64(ret, ret, 0x0f0f0f0full); + flags |= TCG_BSWAP_IZ; + } + tcg_gen_bswap32_i64(ret, ret, flags); + } } void tcg_gen_revbit8_i64(TCGv_i64 ret, TCGv_i64 arg) { - gen_bitswap_i64(ret, arg, 0x5555555555555555ull); - gen_bitswap_i64(ret, ret, 0x3333333333333333ull); - gen_bitswap_i64(ret, ret, 0x0f0f0f0f0f0f0f0full); + if (tcg_op_supported(INDEX_op_revbit8, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit8, ret, arg); + } else if (tcg_op_supported(INDEX_op_revbit64, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit64, ret, arg); + tcg_gen_bswap64_i64(ret, ret); + } else { + gen_bitswap_i64(ret, arg, 0x5555555555555555ull); + gen_bitswap_i64(ret, ret, 0x3333333333333333ull); + gen_bitswap_i64(ret, ret, 0x0f0f0f0f0f0f0f0full); + } } void tcg_gen_revbit64_i64(TCGv_i64 ret, TCGv_i64 arg) { - tcg_gen_revbit8_i64(ret, arg); - tcg_gen_bswap64_i64(ret, ret); + if (tcg_op_supported(INDEX_op_revbit64, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_revbit64, ret, arg); + } else { + tcg_gen_revbit8_i64(ret, arg); + tcg_gen_bswap64_i64(ret, ret); + } } void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) diff --git a/tcg/tcg.c b/tcg/tcg.c index 937d0c8fd7..8a324ce885 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1203,6 +1203,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_qemu_st2, TCGOutOpQemuLdSt2, outop_qemu_st2), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_revbit32, TCGOutOpBswap, outop_revbit32), OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), @@ -1230,6 +1231,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), OUTOP(INDEX_op_ld32u, TCGOutOpLoad, outop_ld32u), OUTOP(INDEX_op_ld32s, TCGOutOpLoad, outop_ld32s), + OUTOP(INDEX_op_revbit8, TCGOutOpUnary, outop_revbit8), + OUTOP(INDEX_op_revbit64, TCGOutOpUnary, outop_revbit64), OUTOP(INDEX_op_st32, TCGOutOpStore, outop_st), }; @@ -2950,6 +2953,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) case INDEX_op_bswap16: case INDEX_op_bswap32: case INDEX_op_bswap64: + case INDEX_op_revbit32: { TCGArg flags = op->args[k]; const char *name = NULL; @@ -5581,6 +5585,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_ctpop: case INDEX_op_neg: case INDEX_op_not: + case INDEX_op_revbit8: + case INDEX_op_revbit64: { const TCGOutOpUnary *out = container_of(all_outop[op->opc], TCGOutOpUnary, base); @@ -5593,6 +5599,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_bswap16: case INDEX_op_bswap32: + case INDEX_op_revbit32: { const TCGOutOpBswap *out = container_of(all_outop[op->opc], TCGOutOpBswap, base); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1b22c70616..1b61668517 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -959,6 +959,18 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out_op_rr(s, INDEX_op_neg, a0, a1); diff --git a/tcg/x86_64/tcg-target.c.inc b/tcg/x86_64/tcg-target.c.inc index 1fc45e4ec6..37acba9045 100644 --- a/tcg/x86_64/tcg-target.c.inc +++ b/tcg/x86_64/tcg-target.c.inc @@ -1290,6 +1290,18 @@ static inline void tcg_out_bswap64(TCGContext *s, int reg) tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); } +static const TCGOutOpUnary outop_revbit8 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBswap outop_revbit32 = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpUnary outop_revbit64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_arithi(TCGContext *s, int c, int r0, tcg_target_long val, int cf) { From d53c3e911c97c007995b79562ed695e5db0d8c29 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 15:36:35 -0700 Subject: [PATCH 598/860] tcg/optimize: Handle revbit{8,32,64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are nearly identical to bswap, so reuse fold_bswap. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 77 +++++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d12babad88..facf4c1e0f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -534,6 +534,20 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, case INDEX_op_bswap64: return bswap64(x); + case INDEX_op_revbit8: + /* Note the host-utils.h revbit8 operates on uint8_t. */ + if (type == TCG_TYPE_I32) { + return bswap32(revbit32(x)); + } + return bswap64(revbit64(x)); + + case INDEX_op_revbit32: + x = revbit32(x); + return y & TCG_BSWAP_OS ? (int32_t)x : x; + + case INDEX_op_revbit64: + return revbit64(x); + case INDEX_op_ext_i32_i64: return (int32_t)x; @@ -1483,7 +1497,26 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) { uint64_t z_mask, o_mask, s_mask; TempOptInfo *t1 = arg_info(op->args[1]); - int flags = op->args[2]; + int flags = 0; + + switch (op->opc) { + case INDEX_op_bswap16: + flags = op->args[2]; + s_mask = INT16_MIN; + break; + case INDEX_op_bswap32: + case INDEX_op_revbit32: + flags = op->args[2]; + s_mask = INT32_MIN; + break; + case INDEX_op_bswap64: + case INDEX_op_revbit8: + case INDEX_op_revbit64: + s_mask = 0; + break; + default: + g_assert_not_reached(); + } if (ti_is_const(t1)) { return tcg_opt_gen_movi(ctx, op, op->args[0], @@ -1491,39 +1524,16 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) ti_const_val(t1), flags)); } - z_mask = t1->z_mask; - o_mask = t1->o_mask; - s_mask = 0; + z_mask = do_constant_folding(op->opc, ctx->type, t1->z_mask, flags); + o_mask = do_constant_folding(op->opc, ctx->type, t1->o_mask, flags); - switch (op->opc) { - case INDEX_op_bswap16: - z_mask = bswap16(z_mask); - o_mask = bswap16(o_mask); - if (flags & TCG_BSWAP_OS) { - z_mask = (int16_t)z_mask; - o_mask = (int16_t)o_mask; - s_mask = INT16_MIN; - } else if (!(flags & TCG_BSWAP_OZ)) { - z_mask |= MAKE_64BIT_MASK(16, 48); + if (flags & TCG_BSWAP_OS) { + /* s_mask set */ + } else { + if (!(flags & TCG_BSWAP_OZ)) { + z_mask |= s_mask << 1; } - break; - case INDEX_op_bswap32: - z_mask = bswap32(z_mask); - o_mask = bswap32(o_mask); - if (flags & TCG_BSWAP_OS) { - z_mask = (int32_t)z_mask; - o_mask = (int32_t)o_mask; - s_mask = INT32_MIN; - } else if (!(flags & TCG_BSWAP_OZ)) { - z_mask |= MAKE_64BIT_MASK(32, 32); - } - break; - case INDEX_op_bswap64: - z_mask = bswap64(z_mask); - o_mask = bswap64(o_mask); - break; - default: - g_assert_not_reached(); + s_mask = 0; } return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); @@ -3104,6 +3114,9 @@ void tcg_optimize(TCGContext *s) case INDEX_op_bswap16: case INDEX_op_bswap32: case INDEX_op_bswap64: + case INDEX_op_revbit8: + case INDEX_op_revbit32: + case INDEX_op_revbit64: done = fold_bswap(&ctx, op); break; case INDEX_op_clz: From a8687b8632a9dfe6dff094615e030480a950ffd0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 22:59:22 +0000 Subject: [PATCH 599/860] tcg/aarch64: Implement revbit{32,64} Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 0afa988087..80995403e4 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2656,12 +2656,28 @@ static const TCGOutOpUnary outop_revbit8 = { .base.static_constraint = C_NotImplemented, }; +static void tgen_revbit32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_insn(s, rr_sf, RBIT, TCG_TYPE_I32, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } +} + static const TCGOutOpBswap outop_revbit32 = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit32, }; +static void tgen_revbit64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_insn(s, rr_sf, RBIT, TCG_TYPE_I64, a0, a1); +} + static const TCGOutOpUnary outop_revbit64 = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit64, }; static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) From 3e73eda07860439e1c0aae8d18f5cd880995990a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 16:33:52 -0700 Subject: [PATCH 600/860] tcg/loongarch64: Import REVBIT insns Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-insn-defs.c.inc | 960 +++++++++++++++------------- 1 file changed, 503 insertions(+), 457 deletions(-) diff --git a/tcg/loongarch64/tcg-insn-defs.c.inc b/tcg/loongarch64/tcg-insn-defs.c.inc index 6bb8656fd8..e907fc755f 100644 --- a/tcg/loongarch64/tcg-insn-defs.c.inc +++ b/tcg/loongarch64/tcg-insn-defs.c.inc @@ -4,7 +4,7 @@ * * This file is auto-generated by genqemutcgdefs from * https://github.com/loongson-community/loongarch-opcodes, - * from commit 7f353fb69bd99ce6edfad7ad63948c4bb526f0bf. + * from commit 1b95bb8c443ad71e266120fd97bd227df2d8ecfc. * DO NOT EDIT. */ @@ -18,6 +18,10 @@ typedef enum { OPC_REVB_2H = 0x00003000, OPC_REVB_2W = 0x00003800, OPC_REVB_D = 0x00003c00, + OPC_REVBIT_4B = 0x00004800, + OPC_REVBIT_8B = 0x00004c00, + OPC_REVBIT_W = 0x00005000, + OPC_REVBIT_D = 0x00005400, OPC_SEXT_H = 0x00005800, OPC_SEXT_B = 0x00005c00, OPC_ADD_W = 0x00100000, @@ -545,14 +549,14 @@ typedef enum { OPC_XVLDI = 0x77e00000, } LoongArchInsn; -static int32_t __attribute__((unused)) -encode_d_slot(LoongArchInsn opc, uint32_t d) +static int32_t __attribute__((unused)) encode_d_slot(LoongArchInsn opc, + uint32_t d) { return opc | d; } -static int32_t __attribute__((unused)) -encode_dj_slots(LoongArchInsn opc, uint32_t d, uint32_t j) +static int32_t __attribute__((unused)) encode_dj_slots(LoongArchInsn opc, + uint32_t d, uint32_t j) { return opc | d | j << 5; } @@ -563,43 +567,43 @@ encode_djk_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k) return opc | d | j << 5 | k << 10; } -static int32_t __attribute__((unused)) -encode_djka_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, - uint32_t a) +static int32_t __attribute__((unused)) encode_djka_slots(LoongArchInsn opc, + uint32_t d, uint32_t j, + uint32_t k, uint32_t a) { return opc | d | j << 5 | k << 10 | a << 15; } -static int32_t __attribute__((unused)) -encode_djkm_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, - uint32_t m) +static int32_t __attribute__((unused)) encode_djkm_slots(LoongArchInsn opc, + uint32_t d, uint32_t j, + uint32_t k, uint32_t m) { return opc | d | j << 5 | k << 10 | m << 16; } -static int32_t __attribute__((unused)) -encode_djkn_slots(LoongArchInsn opc, uint32_t d, uint32_t j, uint32_t k, - uint32_t n) +static int32_t __attribute__((unused)) encode_djkn_slots(LoongArchInsn opc, + uint32_t d, uint32_t j, + uint32_t k, uint32_t n) { return opc | d | j << 5 | k << 10 | n << 18; } -static int32_t __attribute__((unused)) -encode_dk_slots(LoongArchInsn opc, uint32_t d, uint32_t k) +static int32_t __attribute__((unused)) encode_dk_slots(LoongArchInsn opc, + uint32_t d, uint32_t k) { return opc | d | k << 10; } -static int32_t __attribute__((unused)) -encode_dfj_insn(LoongArchInsn opc, TCGReg d, TCGReg fj) +static int32_t __attribute__((unused)) encode_dfj_insn(LoongArchInsn opc, + TCGReg d, TCGReg fj) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); return encode_dj_slots(opc, d, fj & 0x1f); } -static int32_t __attribute__((unused)) -encode_dj_insn(LoongArchInsn opc, TCGReg d, TCGReg j) +static int32_t __attribute__((unused)) encode_dj_insn(LoongArchInsn opc, + TCGReg d, TCGReg j) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -669,9 +673,10 @@ encode_djuk5_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk5) return encode_djk_slots(opc, d, j, uk5); } -static int32_t __attribute__((unused)) -encode_djuk5um5_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +static int32_t __attribute__((unused)) encode_djuk5um5_insn(LoongArchInsn opc, + TCGReg d, TCGReg j, + uint32_t uk5, + uint32_t um5) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -689,9 +694,10 @@ encode_djuk6_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk6) return encode_djk_slots(opc, d, j, uk6); } -static int32_t __attribute__((unused)) -encode_djuk6um6_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +static int32_t __attribute__((unused)) encode_djuk6um6_insn(LoongArchInsn opc, + TCGReg d, TCGReg j, + uint32_t uk6, + uint32_t um6) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -700,16 +706,16 @@ encode_djuk6um6_insn(LoongArchInsn opc, TCGReg d, TCGReg j, uint32_t uk6, return encode_djkm_slots(opc, d, j, uk6, um6); } -static int32_t __attribute__((unused)) -encode_dsj20_insn(LoongArchInsn opc, TCGReg d, int32_t sj20) +static int32_t __attribute__((unused)) encode_dsj20_insn(LoongArchInsn opc, + TCGReg d, int32_t sj20) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(sj20 >= -0x80000 && sj20 <= 0x7ffff); return encode_dj_slots(opc, d, sj20 & 0xfffff); } -static int32_t __attribute__((unused)) -encode_dtj_insn(LoongArchInsn opc, TCGReg d, TCGReg tj) +static int32_t __attribute__((unused)) encode_dtj_insn(LoongArchInsn opc, + TCGReg d, TCGReg tj) { tcg_debug_assert(d >= 0 && d <= 0x1f); tcg_debug_assert(tj >= 0 && tj <= 0x3); @@ -770,16 +776,16 @@ encode_dxjuk3_insn(LoongArchInsn opc, TCGReg d, TCGReg xj, uint32_t uk3) return encode_djk_slots(opc, d, xj & 0x1f, uk3); } -static int32_t __attribute__((unused)) -encode_fdfj_insn(LoongArchInsn opc, TCGReg fd, TCGReg fj) +static int32_t __attribute__((unused)) encode_fdfj_insn(LoongArchInsn opc, + TCGReg fd, TCGReg fj) { tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); tcg_debug_assert(fj >= 0x20 && fj <= 0x3f); return encode_dj_slots(opc, fd & 0x1f, fj & 0x1f); } -static int32_t __attribute__((unused)) -encode_fdj_insn(LoongArchInsn opc, TCGReg fd, TCGReg j) +static int32_t __attribute__((unused)) encode_fdj_insn(LoongArchInsn opc, + TCGReg fd, TCGReg j) { tcg_debug_assert(fd >= 0x20 && fd <= 0x3f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -804,37 +810,37 @@ encode_fdjsk12_insn(LoongArchInsn opc, TCGReg fd, TCGReg j, int32_t sk12) return encode_djk_slots(opc, fd & 0x1f, j, sk12 & 0xfff); } -static int32_t __attribute__((unused)) -encode_sd10k16_insn(LoongArchInsn opc, int32_t sd10k16) +static int32_t __attribute__((unused)) encode_sd10k16_insn(LoongArchInsn opc, + int32_t sd10k16) { tcg_debug_assert(sd10k16 >= -0x2000000 && sd10k16 <= 0x1ffffff); return encode_dk_slots(opc, (sd10k16 >> 16) & 0x3ff, sd10k16 & 0xffff); } -static int32_t __attribute__((unused)) -encode_sd5k16_insn(LoongArchInsn opc, int32_t sd5k16) +static int32_t __attribute__((unused)) encode_sd5k16_insn(LoongArchInsn opc, + int32_t sd5k16) { tcg_debug_assert(sd5k16 >= -0x100000 && sd5k16 <= 0xfffff); return encode_dk_slots(opc, (sd5k16 >> 16) & 0x1f, sd5k16 & 0xffff); } -static int32_t __attribute__((unused)) -encode_tdj_insn(LoongArchInsn opc, TCGReg td, TCGReg j) +static int32_t __attribute__((unused)) encode_tdj_insn(LoongArchInsn opc, + TCGReg td, TCGReg j) { tcg_debug_assert(td >= 0 && td <= 0x3); tcg_debug_assert(j >= 0 && j <= 0x1f); return encode_dj_slots(opc, td, j); } -static int32_t __attribute__((unused)) -encode_ud15_insn(LoongArchInsn opc, uint32_t ud15) +static int32_t __attribute__((unused)) encode_ud15_insn(LoongArchInsn opc, + uint32_t ud15) { tcg_debug_assert(ud15 <= 0x7fff); return encode_d_slot(opc, ud15); } -static int32_t __attribute__((unused)) -encode_vdj_insn(LoongArchInsn opc, TCGReg vd, TCGReg j) +static int32_t __attribute__((unused)) encode_vdj_insn(LoongArchInsn opc, + TCGReg vd, TCGReg j) { tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -974,8 +980,8 @@ encode_vdsj13_insn(LoongArchInsn opc, TCGReg vd, int32_t sj13) return encode_dj_slots(opc, vd & 0x1f, sj13 & 0x1fff); } -static int32_t __attribute__((unused)) -encode_vdvj_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj) +static int32_t __attribute__((unused)) encode_vdvj_insn(LoongArchInsn opc, + TCGReg vd, TCGReg vj) { tcg_debug_assert(vd >= 0x20 && vd <= 0x3f); tcg_debug_assert(vj >= 0x20 && vj <= 0x3f); @@ -1083,8 +1089,8 @@ encode_vdvjvkva_insn(LoongArchInsn opc, TCGReg vd, TCGReg vj, TCGReg vk, return encode_djka_slots(opc, vd & 0x1f, vj & 0x1f, vk & 0x1f, va & 0x1f); } -static int32_t __attribute__((unused)) -encode_xdj_insn(LoongArchInsn opc, TCGReg xd, TCGReg j) +static int32_t __attribute__((unused)) encode_xdj_insn(LoongArchInsn opc, + TCGReg xd, TCGReg j) { tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); tcg_debug_assert(j >= 0 && j <= 0x1f); @@ -1206,8 +1212,8 @@ encode_xdsj13_insn(LoongArchInsn opc, TCGReg xd, int32_t sj13) return encode_dj_slots(opc, xd & 0x1f, sj13 & 0x1fff); } -static int32_t __attribute__((unused)) -encode_xdxj_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj) +static int32_t __attribute__((unused)) encode_xdxj_insn(LoongArchInsn opc, + TCGReg xd, TCGReg xj) { tcg_debug_assert(xd >= 0x20 && xd <= 0x3f); tcg_debug_assert(xj >= 0x20 && xj <= 0x3f); @@ -1316,523 +1322,555 @@ encode_xdxjxkxa_insn(LoongArchInsn opc, TCGReg xd, TCGReg xj, TCGReg xk, } /* Emits the `movgr2scr td, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movgr2scr(TCGContext *s, TCGReg td, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_movgr2scr(TCGContext *s, + TCGReg td, TCGReg j) { tcg_out32(s, encode_tdj_insn(OPC_MOVGR2SCR, td, j)); } /* Emits the `movscr2gr d, tj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movscr2gr(TCGContext *s, TCGReg d, TCGReg tj) +static void __attribute__((unused)) tcg_out_opc_movscr2gr(TCGContext *s, + TCGReg d, TCGReg tj) { tcg_out32(s, encode_dtj_insn(OPC_MOVSCR2GR, d, tj)); } /* Emits the `clz.w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_clz_w(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_clz_w(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CLZ_W, d, j)); } /* Emits the `ctz.w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ctz_w(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_ctz_w(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CTZ_W, d, j)); } /* Emits the `clz.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_clz_d(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_clz_d(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CLZ_D, d, j)); } /* Emits the `ctz.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ctz_d(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_ctz_d(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_CTZ_D, d, j)); } /* Emits the `revb.2h d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_2h(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_revb_2h(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_REVB_2H, d, j)); } /* Emits the `revb.2w d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_2w(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_revb_2w(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_REVB_2W, d, j)); } /* Emits the `revb.d d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_revb_d(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_revb_d(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_REVB_D, d, j)); } +/* Emits the `revbit.4b d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_4b(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_4B, d, j)); +} + +/* Emits the `revbit.8b d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_8b(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_8B, d, j)); +} + +/* Emits the `revbit.w d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_w(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_W, d, j)); +} + +/* Emits the `revbit.d d, j` instruction. */ +static void __attribute__((unused)) tcg_out_opc_revbit_d(TCGContext *s, + TCGReg d, TCGReg j) +{ + tcg_out32(s, encode_dj_insn(OPC_REVBIT_D, d, j)); +} + /* Emits the `sext.h d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sext_h(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_sext_h(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_SEXT_H, d, j)); } /* Emits the `sext.b d, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sext_b(TCGContext *s, TCGReg d, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_sext_b(TCGContext *s, TCGReg d, + TCGReg j) { tcg_out32(s, encode_dj_insn(OPC_SEXT_B, d, j)); } /* Emits the `add.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_add_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_add_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ADD_W, d, j, k)); } /* Emits the `add.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_add_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_add_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ADD_D, d, j, k)); } /* Emits the `sub.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sub_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sub_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SUB_W, d, j, k)); } /* Emits the `sub.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sub_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sub_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SUB_D, d, j, k)); } /* Emits the `slt d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slt(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_slt(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLT, d, j, k)); } /* Emits the `sltu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sltu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sltu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLTU, d, j, k)); } /* Emits the `maskeqz d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_maskeqz(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_maskeqz(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MASKEQZ, d, j, k)); } /* Emits the `masknez d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_masknez(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_masknez(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MASKNEZ, d, j, k)); } /* Emits the `nor d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_nor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_nor(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_NOR, d, j, k)); } /* Emits the `and d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_and(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_and(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_AND, d, j, k)); } /* Emits the `or d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_or(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_or(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_OR, d, j, k)); } /* Emits the `xor d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xor(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_xor(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_XOR, d, j, k)); } /* Emits the `orn d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_orn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_orn(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ORN, d, j, k)); } /* Emits the `andn d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_andn(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_andn(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ANDN, d, j, k)); } /* Emits the `sll.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sll_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sll_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLL_W, d, j, k)); } /* Emits the `srl.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srl_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_srl_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRL_W, d, j, k)); } /* Emits the `sra.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sra_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sra_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRA_W, d, j, k)); } /* Emits the `sll.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sll_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sll_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SLL_D, d, j, k)); } /* Emits the `srl.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srl_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_srl_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRL_D, d, j, k)); } /* Emits the `sra.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sra_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_sra_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_SRA_D, d, j, k)); } /* Emits the `rotr.b d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_b(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_B, d, j, k)); } /* Emits the `rotr.h d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_h(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_H, d, j, k)); } /* Emits the `rotr.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_W, d, j, k)); } /* Emits the `rotr.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotr_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_rotr_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_ROTR_D, d, j, k)); } /* Emits the `mul.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mul_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mul_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MUL_W, d, j, k)); } /* Emits the `mulh.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_W, d, j, k)); } /* Emits the `mulh.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_WU, d, j, k)); } /* Emits the `mul.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mul_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mul_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MUL_D, d, j, k)); } /* Emits the `mulh.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_D, d, j, k)); } /* Emits the `mulh.du d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mulh_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mulh_du(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MULH_DU, d, j, k)); } /* Emits the `div.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_W, d, j, k)); } /* Emits the `mod.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_W, d, j, k)); } /* Emits the `div.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_WU, d, j, k)); } /* Emits the `mod.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_WU, d, j, k)); } /* Emits the `div.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_D, d, j, k)); } /* Emits the `mod.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_D, d, j, k)); } /* Emits the `div.du d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_div_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_div_du(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_DIV_DU, d, j, k)); } /* Emits the `mod.du d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_mod_du(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_mod_du(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_MOD_DU, d, j, k)); } /* Emits the `slli.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_slli_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_SLLI_W, d, j, uk5)); } /* Emits the `slli.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_slli_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_SLLI_D, d, j, uk6)); } /* Emits the `srli.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srli_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_srli_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_SRLI_W, d, j, uk5)); } /* Emits the `srli.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srli_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_srli_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_SRLI_D, d, j, uk6)); } /* Emits the `srai.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srai_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_srai_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_SRAI_W, d, j, uk5)); } /* Emits the `srai.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_srai_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_srai_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_SRAI_D, d, j, uk6)); } /* Emits the `rotri.b d, j, uk3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_b(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk3) +static void __attribute__((unused)) tcg_out_opc_rotri_b(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk3) { tcg_out32(s, encode_djuk3_insn(OPC_ROTRI_B, d, j, uk3)); } /* Emits the `rotri.h d, j, uk4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_h(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk4) +static void __attribute__((unused)) tcg_out_opc_rotri_h(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk4) { tcg_out32(s, encode_djuk4_insn(OPC_ROTRI_H, d, j, uk4)); } /* Emits the `rotri.w d, j, uk5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5) +static void __attribute__((unused)) tcg_out_opc_rotri_w(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk5) { tcg_out32(s, encode_djuk5_insn(OPC_ROTRI_W, d, j, uk5)); } /* Emits the `rotri.d d, j, uk6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_rotri_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6) +static void __attribute__((unused)) tcg_out_opc_rotri_d(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk6) { tcg_out32(s, encode_djuk6_insn(OPC_ROTRI_D, d, j, uk6)); } /* Emits the `bstrins.w d, j, uk5, um5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrins_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +static void __attribute__((unused)) tcg_out_opc_bstrins_w(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk5, + uint32_t um5) { tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRINS_W, d, j, uk5, um5)); } /* Emits the `bstrpick.w d, j, uk5, um5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrpick_w(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk5, - uint32_t um5) +static void __attribute__((unused)) tcg_out_opc_bstrpick_w(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk5, + uint32_t um5) { tcg_out32(s, encode_djuk5um5_insn(OPC_BSTRPICK_W, d, j, uk5, um5)); } /* Emits the `bstrins.d d, j, uk6, um6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrins_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +static void __attribute__((unused)) tcg_out_opc_bstrins_d(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk6, + uint32_t um6) { tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRINS_D, d, j, uk6, um6)); } /* Emits the `bstrpick.d d, j, uk6, um6` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bstrpick_d(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk6, - uint32_t um6) +static void __attribute__((unused)) tcg_out_opc_bstrpick_d(TCGContext *s, + TCGReg d, TCGReg j, + uint32_t uk6, + uint32_t um6) { tcg_out32(s, encode_djuk6um6_insn(OPC_BSTRPICK_D, d, j, uk6, um6)); } /* Emits the `fmov.d fd, fj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fmov_d(TCGContext *s, TCGReg fd, TCGReg fj) +static void __attribute__((unused)) tcg_out_opc_fmov_d(TCGContext *s, TCGReg fd, + TCGReg fj) { tcg_out32(s, encode_fdfj_insn(OPC_FMOV_D, fd, fj)); } /* Emits the `movgr2fr.d fd, j` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movgr2fr_d(TCGContext *s, TCGReg fd, TCGReg j) +static void __attribute__((unused)) tcg_out_opc_movgr2fr_d(TCGContext *s, + TCGReg fd, TCGReg j) { tcg_out32(s, encode_fdj_insn(OPC_MOVGR2FR_D, fd, j)); } /* Emits the `movfr2gr.d d, fj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_movfr2gr_d(TCGContext *s, TCGReg d, TCGReg fj) +static void __attribute__((unused)) tcg_out_opc_movfr2gr_d(TCGContext *s, + TCGReg d, TCGReg fj) { tcg_out32(s, encode_dfj_insn(OPC_MOVFR2GR_D, d, fj)); } /* Emits the `slti d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_slti(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_slti(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_SLTI, d, j, sk12)); } /* Emits the `sltui d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_sltui(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_sltui(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_SLTUI, d, j, sk12)); } /* Emits the `addi.w d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_addi_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_addi_w(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ADDI_W, d, j, sk12)); } /* Emits the `addi.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_addi_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_addi_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ADDI_D, d, j, sk12)); } /* Emits the `cu52i.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_cu52i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_cu52i_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_CU52I_D, d, j, sk12)); } /* Emits the `andi d, j, uk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_andi(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +static void __attribute__((unused)) tcg_out_opc_andi(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk12) { tcg_out32(s, encode_djuk12_insn(OPC_ANDI, d, j, uk12)); } /* Emits the `ori d, j, uk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +static void __attribute__((unused)) tcg_out_opc_ori(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk12) { tcg_out32(s, encode_djuk12_insn(OPC_ORI, d, j, uk12)); } /* Emits the `xori d, j, uk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xori(TCGContext *s, TCGReg d, TCGReg j, uint32_t uk12) +static void __attribute__((unused)) tcg_out_opc_xori(TCGContext *s, TCGReg d, + TCGReg j, uint32_t uk12) { tcg_out32(s, encode_djuk12_insn(OPC_XORI, d, j, uk12)); } @@ -1845,9 +1883,9 @@ tcg_out_opc_vbitsel_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk, TCGReg va) } /* Emits the `xvbitsel.v xd, xj, xk, xa` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvbitsel_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk, - TCGReg xa) +static void __attribute__((unused)) tcg_out_opc_xvbitsel_v(TCGContext *s, + TCGReg xd, TCGReg xj, + TCGReg xk, TCGReg xa) { tcg_out32(s, encode_xdxjxkxa_insn(OPC_XVBITSEL_V, xd, xj, xk, xa)); } @@ -1874,22 +1912,22 @@ tcg_out_opc_addu16i_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) } /* Emits the `lu12i.w d, sj20` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_lu12i_w(TCGContext *s, TCGReg d, int32_t sj20) +static void __attribute__((unused)) tcg_out_opc_lu12i_w(TCGContext *s, TCGReg d, + int32_t sj20) { tcg_out32(s, encode_dsj20_insn(OPC_LU12I_W, d, sj20)); } /* Emits the `cu32i.d d, sj20` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_cu32i_d(TCGContext *s, TCGReg d, int32_t sj20) +static void __attribute__((unused)) tcg_out_opc_cu32i_d(TCGContext *s, TCGReg d, + int32_t sj20) { tcg_out32(s, encode_dsj20_insn(OPC_CU32I_D, d, sj20)); } /* Emits the `pcaddu2i d, sj20` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_pcaddu2i(TCGContext *s, TCGReg d, int32_t sj20) +static void __attribute__((unused)) tcg_out_opc_pcaddu2i(TCGContext *s, + TCGReg d, int32_t sj20) { tcg_out32(s, encode_dsj20_insn(OPC_PCADDU2I, d, sj20)); } @@ -1916,134 +1954,134 @@ tcg_out_opc_pcaddu18i(TCGContext *s, TCGReg d, int32_t sj20) } /* Emits the `ld.b d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_b(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_B, d, j, sk12)); } /* Emits the `ld.h d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_h(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_H, d, j, sk12)); } /* Emits the `ld.w d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_w(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_W, d, j, sk12)); } /* Emits the `ld.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_D, d, j, sk12)); } /* Emits the `st.b d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_b(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_b(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_B, d, j, sk12)); } /* Emits the `st.h d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_h(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_h(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_H, d, j, sk12)); } /* Emits the `st.w d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_w(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_w(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_W, d, j, sk12)); } /* Emits the `st.d d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_st_d(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_st_d(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_ST_D, d, j, sk12)); } /* Emits the `ld.bu d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_bu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_bu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_BU, d, j, sk12)); } /* Emits the `ld.hu d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_hu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_hu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_HU, d, j, sk12)); } /* Emits the `ld.wu d, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ld_wu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_ld_wu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_djsk12_insn(OPC_LD_WU, d, j, sk12)); } /* Emits the `fld.s fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fld_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fld_s(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_S, fd, j, sk12)); } /* Emits the `fst.s fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fst_s(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fst_s(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FST_S, fd, j, sk12)); } /* Emits the `fld.d fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fld_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fld_d(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FLD_D, fd, j, sk12)); } /* Emits the `fst.d fd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fst_d(TCGContext *s, TCGReg fd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_fst_d(TCGContext *s, TCGReg fd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_fdjsk12_insn(OPC_FST_D, fd, j, sk12)); } /* Emits the `vld vd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vld(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_vld(TCGContext *s, TCGReg vd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_vdjsk12_insn(OPC_VLD, vd, j, sk12)); } /* Emits the `vst vd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vst(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_vst(TCGContext *s, TCGReg vd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_vdjsk12_insn(OPC_VST, vd, j, sk12)); } /* Emits the `xvld xd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvld(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_xvld(TCGContext *s, TCGReg xd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_xdjsk12_insn(OPC_XVLD, xd, j, sk12)); } /* Emits the `xvst xd, j, sk12` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvst(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) +static void __attribute__((unused)) tcg_out_opc_xvst(TCGContext *s, TCGReg xd, + TCGReg j, int32_t sk12) { tcg_out32(s, encode_xdjsk12_insn(OPC_XVST, xd, j, sk12)); } @@ -2077,33 +2115,37 @@ tcg_out_opc_vldrepl_b(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk12) } /* Emits the `vstelm.d vd, j, sk8, un1` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_d(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un1) +static void __attribute__((unused)) tcg_out_opc_vstelm_d(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un1) { tcg_out32(s, encode_vdjsk8un1_insn(OPC_VSTELM_D, vd, j, sk8, un1)); } /* Emits the `vstelm.w vd, j, sk8, un2` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_w(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un2) +static void __attribute__((unused)) tcg_out_opc_vstelm_w(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un2) { tcg_out32(s, encode_vdjsk8un2_insn(OPC_VSTELM_W, vd, j, sk8, un2)); } /* Emits the `vstelm.h vd, j, sk8, un3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_h(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un3) +static void __attribute__((unused)) tcg_out_opc_vstelm_h(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un3) { tcg_out32(s, encode_vdjsk8un3_insn(OPC_VSTELM_H, vd, j, sk8, un3)); } /* Emits the `vstelm.b vd, j, sk8, un4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstelm_b(TCGContext *s, TCGReg vd, TCGReg j, int32_t sk8, - uint32_t un4) +static void __attribute__((unused)) tcg_out_opc_vstelm_b(TCGContext *s, + TCGReg vd, TCGReg j, + int32_t sk8, + uint32_t un4) { tcg_out32(s, encode_vdjsk8un4_insn(OPC_VSTELM_B, vd, j, sk8, un4)); } @@ -2137,306 +2179,310 @@ tcg_out_opc_xvldrepl_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk12) } /* Emits the `xvstelm.d xd, j, sk8, un2` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_d(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un2) +static void __attribute__((unused)) tcg_out_opc_xvstelm_d(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un2) { tcg_out32(s, encode_xdjsk8un2_insn(OPC_XVSTELM_D, xd, j, sk8, un2)); } /* Emits the `xvstelm.w xd, j, sk8, un3` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_w(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un3) +static void __attribute__((unused)) tcg_out_opc_xvstelm_w(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un3) { tcg_out32(s, encode_xdjsk8un3_insn(OPC_XVSTELM_W, xd, j, sk8, un3)); } /* Emits the `xvstelm.h xd, j, sk8, un4` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_h(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un4) +static void __attribute__((unused)) tcg_out_opc_xvstelm_h(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un4) { tcg_out32(s, encode_xdjsk8un4_insn(OPC_XVSTELM_H, xd, j, sk8, un4)); } /* Emits the `xvstelm.b xd, j, sk8, un5` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstelm_b(TCGContext *s, TCGReg xd, TCGReg j, int32_t sk8, - uint32_t un5) +static void __attribute__((unused)) tcg_out_opc_xvstelm_b(TCGContext *s, + TCGReg xd, TCGReg j, + int32_t sk8, + uint32_t un5) { tcg_out32(s, encode_xdjsk8un5_insn(OPC_XVSTELM_B, xd, j, sk8, un5)); } /* Emits the `ldx.b d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_b(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_B, d, j, k)); } /* Emits the `ldx.h d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_h(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_H, d, j, k)); } /* Emits the `ldx.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_W, d, j, k)); } /* Emits the `ldx.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_D, d, j, k)); } /* Emits the `stx.b d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_b(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_b(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_B, d, j, k)); } /* Emits the `stx.h d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_h(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_h(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_H, d, j, k)); } /* Emits the `stx.w d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_w(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_w(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_W, d, j, k)); } /* Emits the `stx.d d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_stx_d(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_stx_d(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_STX_D, d, j, k)); } /* Emits the `ldx.bu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_bu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_bu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_BU, d, j, k)); } /* Emits the `ldx.hu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_hu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_hu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_HU, d, j, k)); } /* Emits the `ldx.wu d, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ldx_wu(TCGContext *s, TCGReg d, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_ldx_wu(TCGContext *s, TCGReg d, + TCGReg j, TCGReg k) { tcg_out32(s, encode_djk_insn(OPC_LDX_WU, d, j, k)); } /* Emits the `fldx.s fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fldx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fldx_s(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FLDX_S, fd, j, k)); } /* Emits the `fldx.d fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fldx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fldx_d(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FLDX_D, fd, j, k)); } /* Emits the `fstx.s fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fstx_s(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fstx_s(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FSTX_S, fd, j, k)); } /* Emits the `fstx.d fd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_fstx_d(TCGContext *s, TCGReg fd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_fstx_d(TCGContext *s, TCGReg fd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_fdjk_insn(OPC_FSTX_D, fd, j, k)); } /* Emits the `vldx vd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vldx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_vldx(TCGContext *s, TCGReg vd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_vdjk_insn(OPC_VLDX, vd, j, k)); } /* Emits the `vstx vd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vstx(TCGContext *s, TCGReg vd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_vstx(TCGContext *s, TCGReg vd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_vdjk_insn(OPC_VSTX, vd, j, k)); } /* Emits the `xvldx xd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvldx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_xvldx(TCGContext *s, TCGReg xd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_xdjk_insn(OPC_XVLDX, xd, j, k)); } /* Emits the `xvstx xd, j, k` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvstx(TCGContext *s, TCGReg xd, TCGReg j, TCGReg k) +static void __attribute__((unused)) tcg_out_opc_xvstx(TCGContext *s, TCGReg xd, + TCGReg j, TCGReg k) { tcg_out32(s, encode_xdjk_insn(OPC_XVSTX, xd, j, k)); } /* Emits the `dbar ud15` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_dbar(TCGContext *s, uint32_t ud15) +static void __attribute__((unused)) tcg_out_opc_dbar(TCGContext *s, + uint32_t ud15) { tcg_out32(s, encode_ud15_insn(OPC_DBAR, ud15)); } /* Emits the `jiscr0 sd5k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_jiscr0(TCGContext *s, int32_t sd5k16) +static void __attribute__((unused)) tcg_out_opc_jiscr0(TCGContext *s, + int32_t sd5k16) { tcg_out32(s, encode_sd5k16_insn(OPC_JISCR0, sd5k16)); } /* Emits the `jiscr1 sd5k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_jiscr1(TCGContext *s, int32_t sd5k16) +static void __attribute__((unused)) tcg_out_opc_jiscr1(TCGContext *s, + int32_t sd5k16) { tcg_out32(s, encode_sd5k16_insn(OPC_JISCR1, sd5k16)); } /* Emits the `jirl d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_jirl(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_jirl(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_JIRL, d, j, sk16)); } /* Emits the `b sd10k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_b(TCGContext *s, int32_t sd10k16) +static void __attribute__((unused)) tcg_out_opc_b(TCGContext *s, + int32_t sd10k16) { tcg_out32(s, encode_sd10k16_insn(OPC_B, sd10k16)); } /* Emits the `bl sd10k16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bl(TCGContext *s, int32_t sd10k16) +static void __attribute__((unused)) tcg_out_opc_bl(TCGContext *s, + int32_t sd10k16) { tcg_out32(s, encode_sd10k16_insn(OPC_BL, sd10k16)); } /* Emits the `beq d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_beq(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_beq(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BEQ, d, j, sk16)); } /* Emits the `bne d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bne(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bne(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BNE, d, j, sk16)); } /* Emits the `bgt d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bgt(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bgt(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BGT, d, j, sk16)); } /* Emits the `ble d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_ble(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_ble(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BLE, d, j, sk16)); } /* Emits the `bgtu d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bgtu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bgtu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BGTU, d, j, sk16)); } /* Emits the `bleu d, j, sk16` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_bleu(TCGContext *s, TCGReg d, TCGReg j, int32_t sk16) +static void __attribute__((unused)) tcg_out_opc_bleu(TCGContext *s, TCGReg d, + TCGReg j, int32_t sk16) { tcg_out32(s, encode_djsk16_insn(OPC_BLEU, d, j, sk16)); } /* Emits the `vseq.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_B, vd, vj, vk)); } /* Emits the `vseq.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_H, vd, vj, vk)); } /* Emits the `vseq.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_W, vd, vj, vk)); } /* Emits the `vseq.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vseq_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vseq_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSEQ_D, vd, vj, vk)); } /* Emits the `vsle.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_B, vd, vj, vk)); } /* Emits the `vsle.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_H, vd, vj, vk)); } /* Emits the `vsle.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_W, vd, vj, vk)); } /* Emits the `vsle.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsle_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsle_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLE_D, vd, vj, vk)); } @@ -2470,29 +2516,29 @@ tcg_out_opc_vsle_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vslt.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_B, vd, vj, vk)); } /* Emits the `vslt.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_H, vd, vj, vk)); } /* Emits the `vslt.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_W, vd, vj, vk)); } /* Emits the `vslt.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vslt_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vslt_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLT_D, vd, vj, vk)); } @@ -2526,57 +2572,57 @@ tcg_out_opc_vslt_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vadd.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_B, vd, vj, vk)); } /* Emits the `vadd.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_H, vd, vj, vk)); } /* Emits the `vadd.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_W, vd, vj, vk)); } /* Emits the `vadd.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vadd_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vadd_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VADD_D, vd, vj, vk)); } /* Emits the `vsub.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_B, vd, vj, vk)); } /* Emits the `vsub.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_H, vd, vj, vk)); } /* Emits the `vsub.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_W, vd, vj, vk)); } /* Emits the `vsub.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsub_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsub_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSUB_D, vd, vj, vk)); } @@ -2694,57 +2740,57 @@ tcg_out_opc_vssub_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vmax.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_B, vd, vj, vk)); } /* Emits the `vmax.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_H, vd, vj, vk)); } /* Emits the `vmax.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_W, vd, vj, vk)); } /* Emits the `vmax.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmax_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmax_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMAX_D, vd, vj, vk)); } /* Emits the `vmin.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_B, vd, vj, vk)); } /* Emits the `vmin.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_H, vd, vj, vk)); } /* Emits the `vmin.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_W, vd, vj, vk)); } /* Emits the `vmin.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmin_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmin_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMIN_D, vd, vj, vk)); } @@ -2806,113 +2852,113 @@ tcg_out_opc_vmin_du(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vmul.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_B, vd, vj, vk)); } /* Emits the `vmul.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_H, vd, vj, vk)); } /* Emits the `vmul.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_W, vd, vj, vk)); } /* Emits the `vmul.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vmul_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vmul_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VMUL_D, vd, vj, vk)); } /* Emits the `vsll.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_B, vd, vj, vk)); } /* Emits the `vsll.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_H, vd, vj, vk)); } /* Emits the `vsll.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_W, vd, vj, vk)); } /* Emits the `vsll.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsll_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsll_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSLL_D, vd, vj, vk)); } /* Emits the `vsrl.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_B, vd, vj, vk)); } /* Emits the `vsrl.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_H, vd, vj, vk)); } /* Emits the `vsrl.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_W, vd, vj, vk)); } /* Emits the `vsrl.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsrl_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsrl_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRL_D, vd, vj, vk)); } /* Emits the `vsra.b vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_b(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_b(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_B, vd, vj, vk)); } /* Emits the `vsra.h vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_h(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_h(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_H, vd, vj, vk)); } /* Emits the `vsra.w vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_w(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_w(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_W, vd, vj, vk)); } /* Emits the `vsra.d vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vsra_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vsra_d(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VSRA_D, vd, vj, vk)); } @@ -2974,29 +3020,29 @@ tcg_out_opc_vreplve_d(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg k) } /* Emits the `vand.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vand_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vand_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VAND_V, vd, vj, vk)); } /* Emits the `vor.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vor_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VOR_V, vd, vj, vk)); } /* Emits the `vxor.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vxor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vxor_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VXOR_V, vd, vj, vk)); } /* Emits the `vnor.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vnor_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vnor_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VNOR_V, vd, vj, vk)); } @@ -3009,8 +3055,8 @@ tcg_out_opc_vandn_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) } /* Emits the `vorn.v vd, vj, vk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vorn_v(TCGContext *s, TCGReg vd, TCGReg vj, TCGReg vk) +static void __attribute__((unused)) tcg_out_opc_vorn_v(TCGContext *s, TCGReg vd, + TCGReg vj, TCGReg vk) { tcg_out32(s, encode_vdvjvk_insn(OPC_VORN_V, vd, vj, vk)); } @@ -3324,29 +3370,29 @@ tcg_out_opc_vmini_du(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk5) } /* Emits the `vneg.b vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_b(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_b(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_B, vd, vj)); } /* Emits the `vneg.h vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_h(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_h(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_H, vd, vj)); } /* Emits the `vneg.w vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_w(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_w(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_W, vd, vj)); } /* Emits the `vneg.d vd, vj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vneg_d(TCGContext *s, TCGReg vd, TCGReg vj) +static void __attribute__((unused)) tcg_out_opc_vneg_d(TCGContext *s, TCGReg vd, + TCGReg vj) { tcg_out32(s, encode_vdvj_insn(OPC_VNEG_D, vd, vj)); } @@ -3702,8 +3748,8 @@ tcg_out_opc_vandi_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) } /* Emits the `vori.b vd, vj, uk8` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vori_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) +static void __attribute__((unused)) tcg_out_opc_vori_b(TCGContext *s, TCGReg vd, + TCGReg vj, uint32_t uk8) { tcg_out32(s, encode_vdvjuk8_insn(OPC_VORI_B, vd, vj, uk8)); } @@ -3723,8 +3769,8 @@ tcg_out_opc_vnori_b(TCGContext *s, TCGReg vd, TCGReg vj, uint32_t uk8) } /* Emits the `vldi vd, sj13` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_vldi(TCGContext *s, TCGReg vd, int32_t sj13) +static void __attribute__((unused)) tcg_out_opc_vldi(TCGContext *s, TCGReg vd, + int32_t sj13) { tcg_out32(s, encode_vdsj13_insn(OPC_VLDI, vd, sj13)); } @@ -4325,8 +4371,8 @@ tcg_out_opc_xvand_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) } /* Emits the `xvor.v xd, xj, xk` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvor_v(TCGContext *s, TCGReg xd, TCGReg xj, TCGReg xk) +static void __attribute__((unused)) tcg_out_opc_xvor_v(TCGContext *s, TCGReg xd, + TCGReg xj, TCGReg xk) { tcg_out32(s, encode_xdxjxk_insn(OPC_XVOR_V, xd, xj, xk)); } @@ -4668,29 +4714,29 @@ tcg_out_opc_xvmini_du(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk5) } /* Emits the `xvneg.b xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_b(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_b(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_B, xd, xj)); } /* Emits the `xvneg.h xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_h(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_h(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_H, xd, xj)); } /* Emits the `xvneg.w xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_w(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_w(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_W, xd, xj)); } /* Emits the `xvneg.d xd, xj` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvneg_d(TCGContext *s, TCGReg xd, TCGReg xj) +static void __attribute__((unused)) tcg_out_opc_xvneg_d(TCGContext *s, + TCGReg xd, TCGReg xj) { tcg_out32(s, encode_xdxj_insn(OPC_XVNEG_D, xd, xj)); } @@ -5060,8 +5106,8 @@ tcg_out_opc_xvnori_b(TCGContext *s, TCGReg xd, TCGReg xj, uint32_t uk8) } /* Emits the `xvldi xd, sj13` instruction. */ -static void __attribute__((unused)) -tcg_out_opc_xvldi(TCGContext *s, TCGReg xd, int32_t sj13) +static void __attribute__((unused)) tcg_out_opc_xvldi(TCGContext *s, TCGReg xd, + int32_t sj13) { tcg_out32(s, encode_xdsj13_insn(OPC_XVLDI, xd, sj13)); } From fa70120a71e51358523a1c1d6321a884def054af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 30 Jul 2026 23:52:07 +0000 Subject: [PATCH 601/860] tcg/loongarch64: Implement revbit{8,32,64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Anton Johansson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 34 +++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 97ed51d99c..7d89e80886 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1866,16 +1866,44 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static void tgen_revbit8(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_revbit_4b(s, a0, a1); + } else { + tcg_out_opc_revbit_8b(s, a0, a1); + } +} + static const TCGOutOpUnary outop_revbit8 = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit8; }; +static void tgen_revbit32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_revbit_w(s, a0, a1); + + /* All 32-bit values are computed sign-extended in the register. */ + if (type == TCG_TYPE_I64 && (flags & TCG_BSWAP_OZ)) { + tcg_out_ext32u(s, a0, a0); + } +} + static const TCGOutOpBswap outop_revbit32 = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit32, }; +static void tgen_revbit64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_revbit_d(s, a0, a1); +} + static const TCGOutOpUnary outop_revbit64 = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_revbit64, }; static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) From 48c18cdc9c9f9beadc204895a85870bddf237f2f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Aug 2026 11:45:59 -0700 Subject: [PATCH 602/860] util/cpuinfo-riscv: Detect Zbkb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RISCV_HWPROBE_EXT_ZBKB was introduced in linux 6.10 with the rest of the hwprobe api. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- host/include/riscv64/host/cpuinfo.h | 1 + util/cpuinfo-riscv.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/host/include/riscv64/host/cpuinfo.h b/host/include/riscv64/host/cpuinfo.h index b2b53dbf62..1f047051a3 100644 --- a/host/include/riscv64/host/cpuinfo.h +++ b/host/include/riscv64/host/cpuinfo.h @@ -12,6 +12,7 @@ #define CPUINFO_ZBS (1u << 3) #define CPUINFO_ZICOND (1u << 4) #define CPUINFO_ZVE64X (1u << 5) +#define CPUINFO_ZBKB (1u << 6) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c index 0291b7218a..8c48b1ea91 100644 --- a/util/cpuinfo-riscv.c +++ b/util/cpuinfo-riscv.c @@ -36,7 +36,7 @@ static void sigill_handler(int signo, siginfo_t *si, void *data) /* Called both as constructor and (possibly) via other constructors. */ unsigned __attribute__((constructor)) cpuinfo_init(void) { - unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS + unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS | CPUINFO_ZBKB | CPUINFO_ZICOND | CPUINFO_ZVE64X; unsigned info = cpuinfo; @@ -60,6 +60,9 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) #if defined(__riscv_arch_test) && \ (defined(__riscv_vector) || defined(__riscv_zve64x)) info |= CPUINFO_ZVE64X; +#endif +#if defined(__riscv_arch_test) && defined(__riscv_zbkb) + info |= CPUINFO_ZBKB; #endif left &= ~info; @@ -76,7 +79,8 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0; info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0; info |= pair.value & RISCV_HWPROBE_EXT_ZBS ? CPUINFO_ZBS : 0; - left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS); + info |= pair.value & RISCV_HWPROBE_EXT_ZBKB ? CPUINFO_ZBKB : 0; + left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS | CPUINFO_ZBKB); #ifdef RISCV_HWPROBE_EXT_ZICOND info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0; left &= ~CPUINFO_ZICOND; @@ -131,6 +135,15 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) left &= ~CPUINFO_ZBS; } + if (left & CPUINFO_ZBKB) { + /* Probe for Zbkb: brev8 zero,zero. */ + got_sigill = 0; + asm volatile(".insn i 0x13, 5, zero, zero, 0x687" + : : : "memory"); + info |= got_sigill ? 0 : CPUINFO_ZBKB; + left &= ~CPUINFO_ZBKB; + } + if (left & CPUINFO_ZICOND) { /* Probe for Zicond: czero.eqz zero,zero,zero. */ got_sigill = 0; From 9d2e171fd606dc7c7b93bd8648a40e3cf745b22a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Aug 2026 11:55:09 -0700 Subject: [PATCH 603/860] tcg/riscv64: Implement revbit8 Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson --- tcg/riscv64/tcg-target.c.inc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tcg/riscv64/tcg-target.c.inc b/tcg/riscv64/tcg-target.c.inc index 687146e0b0..8fd32644fe 100644 --- a/tcg/riscv64/tcg-target.c.inc +++ b/tcg/riscv64/tcg-target.c.inc @@ -246,6 +246,9 @@ typedef enum { OPC_XNOR = 0x40004033, OPC_ZEXT_H = 0x0800403b, + /* Zbkb: Bit Manipulation for Cryptography */ + OPC_BREV8 = 0x68705013, + /* Zicond: integer conditional operations */ OPC_CZERO_EQZ = 0x0e005033, OPC_CZERO_NEZ = 0x0e007033, @@ -2469,8 +2472,20 @@ static const TCGOutOpUnary outop_bswap64 = { .out_rr = tgen_bswap64, }; +static TCGConstraintSetIndex cset_revbit8(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBKB ? C_O1_I1(r, r) : C_NotImplemented; +} + +static void tgen_revbit8(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_imm(s, OPC_BREV8, a0, a1, 0); +} + static const TCGOutOpUnary outop_revbit8 = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_revbit8, + .out_rr = tgen_revbit8, }; static const TCGOutOpBswap outop_revbit32 = { From 2458f0cab5aaa21117f2a151e2f4f1e7d4f9d0ce Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Aug 2026 12:26:57 -0700 Subject: [PATCH 604/860] tests/tcg/loongarch64: Tidy test_bit.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use one macro for all test templates. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tests/tcg/loongarch64/test_bit.c | 69 +++++++------------------------- 1 file changed, 15 insertions(+), 54 deletions(-) diff --git a/tests/tcg/loongarch64/test_bit.c b/tests/tcg/loongarch64/test_bit.c index a6d9904909..69b2eaac78 100644 --- a/tests/tcg/loongarch64/test_bit.c +++ b/tests/tcg/loongarch64/test_bit.c @@ -2,62 +2,23 @@ #include #define ARRAY_SIZE(X) (sizeof(X) / sizeof(*(X))) -#define TEST_CLO(N) \ -static uint64_t test_clo_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("clo."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ + +#define TEST(C, I) \ +static uint64_t test_##C(uint64_t rj) \ +{ \ + uint64_t rd; \ + asm volatile(I " %0, %1\n\t" : "=r"(rd) : "r"(rj)); \ + return rd; \ } -#define TEST_CLZ(N) \ -static uint64_t test_clz_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("clz."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ -} - -#define TEST_CTO(N) \ -static uint64_t test_cto_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("cto."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ -} - -#define TEST_CTZ(N) \ -static uint64_t test_ctz_##N(uint64_t rj) \ -{ \ - uint64_t rd = 0; \ - \ - asm volatile("ctz."#N" %0, %1\n\t" \ - : "=r"(rd) \ - : "r"(rj) \ - : ); \ - return rd; \ -} - -TEST_CLO(w) -TEST_CLO(d) -TEST_CLZ(w) -TEST_CLZ(d) -TEST_CTO(w) -TEST_CTO(d) -TEST_CTZ(w) -TEST_CTZ(d) +TEST(clo_w, "clo.w") +TEST(clo_d, "clo.d") +TEST(clz_w, "clz.w") +TEST(clz_d, "clz.d") +TEST(cto_w, "cto.w") +TEST(cto_d, "cto.d") +TEST(ctz_w, "ctz.w") +TEST(ctz_d, "ctz.d") struct vector { uint64_t (*func)(uint64_t); From ffc27ce9be6751a12c30f9a913d1f41003a74962 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Aug 2026 12:42:54 -0700 Subject: [PATCH 605/860] tests/tcg/loongarch64: Add bitrev smoke tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tests/tcg/loongarch64/test_bit.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/tcg/loongarch64/test_bit.c b/tests/tcg/loongarch64/test_bit.c index 69b2eaac78..65cd6598c9 100644 --- a/tests/tcg/loongarch64/test_bit.c +++ b/tests/tcg/loongarch64/test_bit.c @@ -19,6 +19,10 @@ TEST(cto_w, "cto.w") TEST(cto_d, "cto.d") TEST(ctz_w, "ctz.w") TEST(ctz_d, "ctz.d") +TEST(bitrev_4b, "bitrev.4b") +TEST(bitrev_8b, "bitrev.8b") +TEST(bitrev_w, "bitrev.w") +TEST(bitrev_d, "bitrev.d") struct vector { uint64_t (*func)(uint64_t); @@ -35,6 +39,10 @@ static struct vector vectors[] = { {test_cto_d, 0xabd28a64000000, 0}, {test_ctz_w, 0xfaffff42392476ab, 0}, {test_ctz_d, 0xabd28a64000000, 26}, + {test_bitrev_4b, 0xdeadbeef11223344, 0xffffffff8844cc22}, + {test_bitrev_8b, 0x1122334455667788, 0x8844cc22aa66ee11}, + {test_bitrev_w, 0xdeadbeef89abcdef, 0xfffffffff7b3d591}, + {test_bitrev_d, 0x0123456789abcdef, 0xf7b3d591e6a2c480}, }; int main() From 90aa356edcf1d7c1d4453269a0a8b0e667aa964f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 15:06:36 -0700 Subject: [PATCH 606/860] tcg: Add integer min/max opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have these for vectors; replicate for integers. Reviewed-by: Alex Bennée Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 12 ++++++++ include/tcg/tcg-opc.h | 4 +++ tcg/aarch64/tcg-target.c.inc | 16 +++++++++++ tcg/loongarch64/tcg-target.c.inc | 16 +++++++++++ tcg/ppc64/tcg-target.c.inc | 16 +++++++++++ tcg/riscv64/tcg-target.c.inc | 16 +++++++++++ tcg/s390x/tcg-target.c.inc | 16 +++++++++++ tcg/sparc64/tcg-target.c.inc | 16 +++++++++++ tcg/tcg-op.c | 48 ++++++++++++++++++++++++++------ tcg/tcg.c | 8 ++++++ tcg/tci/tcg-target.c.inc | 16 +++++++++++ tcg/x86_64/tcg-target.c.inc | 16 +++++++++++ 12 files changed, 192 insertions(+), 8 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index f2e9255dd9..88d48a1f99 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -317,6 +317,18 @@ Arithmetic pass 0 to *nh* to make a simple zero-extension of *nl*, so overflow should never occur. + * - smax *t0*, *t1*, *t2* + + umax *t0*, *t1*, *t2* + + - | *t0* = MAX(*t1*, *t2*), for signed and unsigned integers. + + * - smin *t0*, *t1*, *t2* + + umin *t0*, *t1*, *t2* + + - | *t0* = MIN(*t1*, *t2*), for signed and unsigned integers. + Logical ------- diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 13c7f17f76..f3a81d5d7f 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -89,11 +89,15 @@ DEF(setcond, 1, 2, 1, TCG_OPF_INT) DEF(sextract, 1, 1, 2, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(shr, 1, 2, 0, TCG_OPF_INT) +DEF(smax, 1, 2, 0, TCG_OPF_INT) +DEF(smin, 1, 2, 0, TCG_OPF_INT) DEF(st8, 0, 2, 1, TCG_OPF_INT) DEF(st16, 0, 2, 1, TCG_OPF_INT) DEF(st32, 0, 2, 1, TCG_OPF_INT) DEF(st, 0, 2, 1, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) +DEF(umax, 1, 2, 0, TCG_OPF_INT) +DEF(umin, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) DEF(addco, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 80995403e4..f5c185bbf4 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2592,6 +2592,22 @@ static void tcg_out_set_borrow(TCGContext *s) TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 7d89e80886..f65496a040 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1804,6 +1804,22 @@ static void tcg_out_set_borrow(TCGContext *s) g_assert_not_reached(); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/ppc64/tcg-target.c.inc b/tcg/ppc64/tcg-target.c.inc index 07dff67e84..cd1c234367 100644 --- a/tcg/ppc64/tcg-target.c.inc +++ b/tcg/ppc64/tcg-target.c.inc @@ -3281,6 +3281,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out32(s, ADDIC | TAI(TCG_REG_R0, TCG_REG_R0, 0)); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/riscv64/tcg-target.c.inc b/tcg/riscv64/tcg-target.c.inc index 8fd32644fe..723c21b3da 100644 --- a/tcg/riscv64/tcg-target.c.inc +++ b/tcg/riscv64/tcg-target.c.inc @@ -2404,6 +2404,22 @@ static void tcg_out_set_borrow(TCGContext *s) g_assert_not_reached(); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index c481745c3f..4d1a779c47 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2950,6 +2950,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out_insn(s, RR, CLR, TCG_REG_R0, TCG_REG_R0); /* cc = 0 */ } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index d6ed9d3362..35cd14a5b6 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1917,6 +1917,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out_set_carry(s); /* borrow == carry */ } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index c302a484cd..e28944cf72 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1294,22 +1294,38 @@ void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg) void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_smin, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); + } } void tcg_gen_umin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_umin, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b); + } } void tcg_gen_smax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_smax, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a); + } } void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) { - tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_umax, ret, a, b); + } else { + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a); + } } void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) @@ -2473,22 +2489,38 @@ void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_smin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_smin, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_smin, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b); + } } void tcg_gen_umin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b); + if (tcg_op_supported(INDEX_op_umin, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_umin, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b); + } } void tcg_gen_smax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_smax, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_smax, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a); + } } void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { - tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a); + if (tcg_op_supported(INDEX_op_umax, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_umax, ret, a, b); + } else { + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a); + } } void tcg_gen_abs_i64(TCGv_i64 ret, TCGv_i64 a) diff --git a/tcg/tcg.c b/tcg/tcg.c index 8a324ce885..489df0e738 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1211,6 +1211,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), + OUTOP(INDEX_op_smax, TCGOutOpBinary, outop_smax), + OUTOP(INDEX_op_smin, TCGOutOpBinary, outop_smin), OUTOP(INDEX_op_st, TCGOutOpStore, outop_st), OUTOP(INDEX_op_st8, TCGOutOpStore, outop_st8), OUTOP(INDEX_op_st16, TCGOutOpStore, outop_st16), @@ -1220,6 +1222,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_subbo, TCGOutOpAddSubCarry, outop_subbo), /* subb1o is implemented with set_borrow + subbio */ OUTOP(INDEX_op_subb1o, TCGOutOpAddSubCarry, outop_subbio), + OUTOP(INDEX_op_umax, TCGOutOpBinary, outop_umax), + OUTOP(INDEX_op_umin, TCGOutOpBinary, outop_umin), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), [INDEX_op_goto_ptr] = &outop_goto_ptr, @@ -5518,6 +5522,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: + case INDEX_op_smax: + case INDEX_op_smin: + case INDEX_op_umax: + case INDEX_op_umin: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1b61668517..4cd1c1431c 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -894,6 +894,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */ } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/x86_64/tcg-target.c.inc b/tcg/x86_64/tcg-target.c.inc index 37acba9045..2c8f1f3e58 100644 --- a/tcg/x86_64/tcg-target.c.inc +++ b/tcg/x86_64/tcg-target.c.inc @@ -2977,6 +2977,22 @@ static void tcg_out_set_borrow(TCGContext *s) tcg_out8(s, OPC_STC); } +static const TCGOutOpBinary outop_smax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_smin = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umax = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_umin = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { From db9be9b136cb5a68e8d545d87e9ee3afbb6bbe0d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 15:42:35 -0700 Subject: [PATCH 607/860] tcg/optimize: Handle min/max opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index facf4c1e0f..d291c844ca 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -597,6 +597,30 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (uint64_t)x % ((uint64_t)y ? : 1); + case INDEX_op_smax: + if (type == TCG_TYPE_I32) { + return MAX((int32_t)x, (int32_t)y); + } + return MAX((int64_t)x, (int64_t)y); + + case INDEX_op_smin: + if (type == TCG_TYPE_I32) { + return MIN((int32_t)x, (int32_t)y); + } + return MIN((int64_t)x, (int64_t)y); + + case INDEX_op_umax: + if (type == TCG_TYPE_I32) { + return MAX((uint32_t)x, (uint32_t)y); + } + return MAX((uint64_t)x, (uint64_t)y); + + case INDEX_op_umin: + if (type == TCG_TYPE_I32) { + return MIN((uint32_t)x, (uint32_t)y); + } + return MIN((uint64_t)x, (uint64_t)y); + default: g_assert_not_reached(); } @@ -2101,6 +2125,16 @@ static bool fold_mb(OptContext *ctx, TCGOp *op) return true; } +static bool fold_minmax(OptContext *ctx, TCGOp *op, uint64_t bound) +{ + if (fold_const2_commutative(ctx, op) || + fold_xi_to_i(ctx, op, bound) || + fold_xx_to_x(ctx, op)) { + return true; + } + return finish_folding(ctx, op); +} + static bool fold_mov(OptContext *ctx, TCGOp *op) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); @@ -3258,6 +3292,14 @@ void tcg_optimize(TCGContext *s) case INDEX_op_sextract: done = fold_sextract(&ctx, op); break; + case INDEX_op_smax: + done = fold_minmax(&ctx, op, (ctx.type == TCG_TYPE_I32 + ? INT32_MAX : INT64_MAX)); + break; + case INDEX_op_smin: + done = fold_minmax(&ctx, op, (ctx.type == TCG_TYPE_I32 + ? INT32_MIN : INT64_MIN)); + break; case INDEX_op_sub: done = fold_sub(&ctx, op); break; @@ -3273,6 +3315,16 @@ void tcg_optimize(TCGContext *s) case INDEX_op_sub_vec: done = fold_sub_vec(&ctx, op); break; + case INDEX_op_umax: + /* + * Note that 32-bit constants are stored sign extended, + * so (int32_t)UINT32_MAX == -1. + */ + done = fold_minmax(&ctx, op, -1); + break; + case INDEX_op_umin: + done = fold_minmax(&ctx, op, 0); + break; case INDEX_op_xor: case INDEX_op_xor_vec: done = fold_xor(&ctx, op); From 761bfe07b2c954251e25d636e2febb1c2a2e9eea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 16:01:41 -0700 Subject: [PATCH 608/860] util/cpuinfo-aarch64: Detect FEAT_CSSC Signed-off-by: Richard Henderson --- host/include/aarch64/host/cpuinfo.h | 1 + util/cpuinfo-aarch64.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/host/include/aarch64/host/cpuinfo.h b/host/include/aarch64/host/cpuinfo.h index fe671534e4..c32c04dade 100644 --- a/host/include/aarch64/host/cpuinfo.h +++ b/host/include/aarch64/host/cpuinfo.h @@ -12,6 +12,7 @@ #define CPUINFO_AES (1u << 3) #define CPUINFO_PMULL (1u << 4) #define CPUINFO_BTI (1u << 5) +#define CPUINFO_CSSC (1u << 6) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/util/cpuinfo-aarch64.c b/util/cpuinfo-aarch64.c index 288074c08f..4ce6deb7c8 100644 --- a/util/cpuinfo-aarch64.c +++ b/util/cpuinfo-aarch64.c @@ -72,6 +72,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); info |= (hwcap2 & HWCAP2_BTI ? CPUINFO_BTI : 0); + info |= (hwcap2 & HWCAP2_CSSC ? CPUINFO_CSSC : 0); #endif #ifdef CONFIG_DARWIN info |= sysctl_for_bool("hw.optional.arm.FEAT_LSE") * CPUINFO_LSE; @@ -79,6 +80,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) info |= sysctl_for_bool("hw.optional.arm.FEAT_AES") * CPUINFO_AES; info |= sysctl_for_bool("hw.optional.arm.FEAT_PMULL") * CPUINFO_PMULL; info |= sysctl_for_bool("hw.optional.arm.FEAT_BTI") * CPUINFO_BTI; + info |= sysctl_for_bool("hw.optional.arm.FEAT_CSSC") * CPUINFO_CSSC; #endif #if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) int mib[2]; From 100ec60528d389a20fa5c4323a2b35a6f5b9fd3c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 16:40:17 -0700 Subject: [PATCH 609/860] tcg/aarch64: Implement min/max with FEAT_CSSC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-con-set.h | 2 + tcg/aarch64/tcg-target-con-str.h | 2 + tcg/aarch64/tcg-target.c.inc | 103 +++++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/tcg/aarch64/tcg-target-con-set.h b/tcg/aarch64/tcg-target-con-set.h index d0622e65fb..dd84a9af58 100644 --- a/tcg/aarch64/tcg-target-con-set.h +++ b/tcg/aarch64/tcg-target-con-set.h @@ -24,6 +24,8 @@ C_O1_I2(r, r, rAL) C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rL) +C_O1_I2(r, r, rS) +C_O1_I2(r, r, rU) C_O1_I2(r, rZ, rA) C_O1_I2(r, rz, rMZ) C_O1_I2(r, rz, rz) diff --git a/tcg/aarch64/tcg-target-con-str.h b/tcg/aarch64/tcg-target-con-str.h index 48e1722c68..fef75e8a76 100644 --- a/tcg/aarch64/tcg-target-con-str.h +++ b/tcg/aarch64/tcg-target-con-str.h @@ -21,4 +21,6 @@ CONST('L', TCG_CT_CONST_LIMM) CONST('M', TCG_CT_CONST_MONE) CONST('O', TCG_CT_CONST_ORRI) CONST('N', TCG_CT_CONST_ANDI) +CONST('S', TCG_CT_CONST_S8) +CONST('U', TCG_CT_CONST_U8) CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index f5c185bbf4..55b3be0b96 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -152,6 +152,8 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, #define TCG_CT_CONST_ORRI 0x1000 #define TCG_CT_CONST_ANDI 0x2000 #define TCG_CT_CONST_CMP 0x4000 +#define TCG_CT_CONST_S8 0x8000 +#define TCG_CT_CONST_U8 0x10000 #define ALL_GENERAL_REGS 0xffffffffu #define ALL_VECTOR_REGS 0xffffffff00000000ull @@ -320,6 +322,12 @@ static bool tcg_target_const_match(int64_t val, int ct, if ((ct & TCG_CT_CONST_LIMM) && is_limm(val)) { return 1; } + if ((ct & TCG_CT_CONST_S8) && val == (int8_t)val) { + return 1; + } + if ((ct & TCG_CT_CONST_U8) && val == (uint8_t)val) { + return 1; + } if ((ct & TCG_CT_CONST_ZERO) && val == 0) { return 1; } @@ -472,6 +480,12 @@ typedef enum { Iaddsub_imm_SUBI = 0x51000000, Iaddsub_imm_SUBSI = 0x71000000, + /* Min/max immediate instructions. */ + Iminmax_imm_SMAXI = 0x11c00000, + Iminmax_imm_UMAXI = 0x11c40000, + Iminmax_imm_SMINI = 0x11c80000, + Iminmax_imm_UMINI = 0x11cc0000, + /* Bitfield instructions. */ Ibitfield_BFM = 0x33000000, Ibitfield_SBFM = 0x13000000, @@ -533,6 +547,10 @@ typedef enum { Irrr_UMULH = 0x9bc07c00, Irrr_UDIV = 0x1ac00800, Irrr_SDIV = 0x1ac00c00, + Irrr_SMAX = 0x1ac00600, + Irrr_UMAX = 0x1ac00640, + Irrr_SMIN = 0x1ac00680, + Irrr_UMIN = 0x1ac006c0, /* Data-processing (3 source) instructions. */ Irrrr_MADD = 0x1b000000, @@ -737,6 +755,13 @@ static void tcg_out_insn_addsub_imm(TCGContext *s, AArch64Insn insn, tcg_out32(s, insn | ext << 31 | aimm << 10 | rn << 5 | rd); } +static void tcg_out_insn_minmax_imm(TCGContext *s, AArch64Insn insn, + TCGType ext, TCGReg rd, TCGReg rn, + uint8_t imm) +{ + tcg_out32(s, insn | ext << 31 | imm << 10 | rn << 5 | rd); +} + /* This function can be used for both 3.4.2 (Bitfield) and 3.4.4 (Logical immediate). Both insn groups have N, IMMR and IMMS fields that feed the DecodeBitMasks pseudo function. */ @@ -2592,20 +2617,90 @@ static void tcg_out_set_borrow(TCGContext *s) TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR); } +static TCGConstraintSetIndex cset_sminmax(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_CSSC ? C_O1_I2(r, r, rS) : C_NotImplemented; +} + +static void tgen_smax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, SMAX, type, a0, a1, a2); +} + +static void tgen_smaxi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, SMAXI, type, a0, a1, a2); +} + static const TCGOutOpBinary outop_smax = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_sminmax, + .out_rrr = tgen_smax, + .out_rri = tgen_smaxi, }; +static void tgen_smin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, SMIN, type, a0, a1, a2); +} + +static void tgen_smini(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, SMINI, type, a0, a1, a2); +} + static const TCGOutOpBinary outop_smin = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_sminmax, + .out_rrr = tgen_smin, + .out_rri = tgen_smini, }; +static TCGConstraintSetIndex cset_uminmax(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_CSSC ? C_O1_I2(r, r, rU) : C_NotImplemented; +} + +static void tgen_umax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, UMAX, type, a0, a1, a2); +} + +static void tgen_umaxi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, UMAXI, type, a0, a1, a2); +} + static const TCGOutOpBinary outop_umax = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_uminmax, + .out_rrr = tgen_umax, + .out_rri = tgen_umaxi, }; +static void tgen_umin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, rrr, UMIN, type, a0, a1, a2); +} + +static void tgen_umini(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, minmax_imm, UMINI, type, a0, a1, a2); +} + static const TCGOutOpBinary outop_umin = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_uminmax, + .out_rrr = tgen_umin, + .out_rri = tgen_umini, }; static void tgen_xor(TCGContext *s, TCGType type, From 20eaf9a54abde6c55a7dacb5888b1deb8d68eb75 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 16:50:03 -0700 Subject: [PATCH 610/860] target/riscv64: Implement min/max with Zbb Signed-off-by: Richard Henderson --- tcg/riscv64/tcg-target.c.inc | 44 ++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/tcg/riscv64/tcg-target.c.inc b/tcg/riscv64/tcg-target.c.inc index 723c21b3da..2ce9d47a63 100644 --- a/tcg/riscv64/tcg-target.c.inc +++ b/tcg/riscv64/tcg-target.c.inc @@ -233,6 +233,10 @@ typedef enum { OPC_CPOPW = 0x6020101b, OPC_CTZ = 0x60101013, OPC_CTZW = 0x6010101b, + OPC_MAX = 0x0a006033, + OPC_MAXU = 0x0a007033, + OPC_MIN = 0x0a004033, + OPC_MINU = 0x0a005033, OPC_ORN = 0x40006033, OPC_REV8 = 0x6b805013, OPC_ROL = 0x60001033, @@ -2404,20 +2408,52 @@ static void tcg_out_set_borrow(TCGContext *s) g_assert_not_reached(); } +static void tgen_smax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MAX, a0, a1, a2); +} + static const TCGOutOpBinary outop_smax = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_smax, }; +static void tgen_smin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MIN, a0, a1, a2); +} + static const TCGOutOpBinary outop_smin = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_smin, }; +static void tgen_umax(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MAXU, a0, a1, a2); +} + static const TCGOutOpBinary outop_umax = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_umax, }; +static void tgen_umin(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MINU, a0, a1, a2); +} + static const TCGOutOpBinary outop_umin = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_umin, }; static void tgen_xor(TCGContext *s, TCGType type, From 1643cf8962f6512dfa06db0299b9728fa51cd99d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 17:03:38 -0700 Subject: [PATCH 611/860] tcg/aarch64: Implement ctpop with FEAT_CSSC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 55b3be0b96..1f784e8d46 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -535,6 +535,7 @@ typedef enum { /* Data-processing (1 source) instructions. */ Irr_sf_CLZ = 0x5ac01000, + Irr_sf_CNT = 0x5ac01c00, Irr_sf_RBIT = 0x5ac00000, Irr_sf_REV = 0x5ac00000, /* + size << 10 */ @@ -2251,8 +2252,20 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_CSSC ? C_O1_I1(r, r) : C_NotImplemented; +} + +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_insn(s, rr_sf, CNT, type, a0, a1); +} + static const TCGOutOpUnary outop_ctpop = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, }; static void tgen_ctz(TCGContext *s, TCGType type, From 8ad4e32be0dd281bf4f1b849be588fee7058de67 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 17:19:20 -0700 Subject: [PATCH 612/860] tcg/aarch64: Use CTZ from FEAT_CSSC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already have an expansion of CTZ using RBIT+CLZ, but use the new insn with FEAT_CSSC is present. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 43 +++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 1f784e8d46..1e64c5994e 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -535,6 +535,7 @@ typedef enum { /* Data-processing (1 source) instructions. */ Irr_sf_CLZ = 0x5ac01000, + Irr_sf_CTZ = 0x5ac01800, Irr_sf_CNT = 0x5ac01c00, Irr_sf_RBIT = 0x5ac00000, Irr_sf_REV = 0x5ac00000, /* + size << 10 */ @@ -2213,24 +2214,30 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; -static void tgen_clz(TCGContext *s, TCGType type, - TCGReg a0, TCGReg a1, TCGReg a2) +static void tgen_clzctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, AArch64Insn insn) { tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); - tcg_out_insn(s, rr_sf, CLZ, type, TCG_REG_TMP0, a1); + tcg_out_insn_rr_sf(s, insn, type, TCG_REG_TMP0, a1); tcg_out_insn(s, csel, CSEL, type, a0, TCG_REG_TMP0, a2, TCG_COND_NE); } -static void tgen_clzi(TCGContext *s, TCGType type, - TCGReg a0, TCGReg a1, tcg_target_long a2) +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_clzctz(s, type, a0, a1, a2, Irr_sf_CLZ); +} + +static void tgen_clzctzi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, AArch64Insn insn) { if (a2 == (type == TCG_TYPE_I32 ? 32 : 64)) { - tcg_out_insn(s, rr_sf, CLZ, type, a0, a1); + tcg_out_insn_rr_sf(s, insn, type, a0, a1); return; } tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); - tcg_out_insn(s, rr_sf, CLZ, type, a0, a1); + tcg_out_insn_rr_sf(s, insn, type, a0, a1); switch (a2) { case -1: @@ -2246,6 +2253,12 @@ static void tgen_clzi(TCGContext *s, TCGType type, } } +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_clzctzi(s, type, a0, a1, a2, Irr_sf_CLZ); +} + static const TCGOutOpBinary outop_clz = { .base.static_constraint = C_O1_I2(r, r, rAL), .out_rrr = tgen_clz, @@ -2271,15 +2284,23 @@ static const TCGOutOpUnary outop_ctpop = { static void tgen_ctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); - tgen_clz(s, type, a0, TCG_REG_TMP0, a2); + if (cpuinfo & CPUINFO_CSSC) { + tgen_clzctz(s, type, a0, a1, a2, Irr_sf_CTZ); + } else { + tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); + tgen_clzctz(s, type, a0, TCG_REG_TMP0, a2, Irr_sf_CLZ); + } } static void tgen_ctzi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, tcg_target_long a2) { - tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); - tgen_clzi(s, type, a0, TCG_REG_TMP0, a2); + if (cpuinfo & CPUINFO_CSSC) { + tgen_clzctzi(s, type, a0, a1, a2, Irr_sf_CTZ); + } else { + tcg_out_insn(s, rr_sf, RBIT, type, TCG_REG_TMP0, a1); + tgen_clzctzi(s, type, a0, TCG_REG_TMP0, a2, Irr_sf_CLZ); + } } static const TCGOutOpBinary outop_ctz = { From a5572e5214771b288bd7234c48cfd513b1eac81f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 09:54:23 -0700 Subject: [PATCH 613/860] target/riscv: Improve riscv_has_ext MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constify the env pointer and return bool. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- 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 c9dfa7daff..376cff656f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -615,7 +615,7 @@ struct RISCVCPUClass { RISCVCPUDef *def; }; -static inline int riscv_has_ext(CPURISCVState *env, uint32_t ext) +static inline bool riscv_has_ext(const CPURISCVState *env, uint32_t ext) { return (env->misa_ext & ext) != 0; } From a854350af144692f0efc6cd8f1a6254898e89866 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 10:49:24 -0700 Subject: [PATCH 614/860] disas/capstone: Allow for cap_insn_unit > length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cap_insn_unit is designed for targets like arm thumb2 and s390x where 4 and 6-byte insns are displayed in 2-byte chunks. For riscv, we prefer 4-byte insns to display as one 4-byte unit, rather than 2x 2-byte units. So we will want to set cap_insn_unit to 4, but allow for insns that are smaller than 4. Emit padding to match. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- disas/capstone.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/disas/capstone.c b/disas/capstone.c index fe3efb0d3c..8fb8d0724c 100644 --- a/disas/capstone.c +++ b/disas/capstone.c @@ -107,8 +107,9 @@ static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn, { fprintf_function print = info->fprintf_func; FILE *stream = info->stream; + int unit = MIN(info->cap_insn_unit, n - i); - switch (info->cap_insn_unit) { + switch (unit) { case 4: if (info->endian == BFD_ENDIAN_BIG) { for (; i < n; i += 4) { @@ -140,6 +141,11 @@ static void cap_dump_insn_units(disassemble_info *info, cs_insn *insn, } break; } + + if (unit < info->cap_insn_unit) { + int width = (info->cap_insn_unit - unit) * 2; + print(stream, "%*s", width, ""); + } } static void cap_dump_insn(disassemble_info *info, cs_insn *insn) From 09d7b5d49ad6dff06c67ae61993fa5657f08dd7c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 11:23:07 -0700 Subject: [PATCH 615/860] target/riscv: Enable disassembly via capstone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In capstone v5, riscv support is spare, but v6 is pretty good. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- disas/capstone.c | 19 ++++++++++ include/disas/capstone.h | 34 +++++++++++++++++ target/riscv/cpu.c | 81 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/disas/capstone.c b/disas/capstone.c index 8fb8d0724c..78062339d0 100644 --- a/disas/capstone.c +++ b/disas/capstone.c @@ -49,6 +49,20 @@ static const cs_opt_skipdata cap_skipdata_s390x = { .callback = cap_skipdata_s390x_cb }; +/* Similarly for RISCV */ +static size_t CAPSTONE_API +cap_skipdata_riscv_cb(const uint8_t *code, size_t code_size, + size_t offset, void *user_data) +{ + /* See insn_len() from target/riscv/internals.h */ + return (code[offset] & 3) == 3 ? 4 : 2; +} + +static const cs_opt_skipdata cap_skipdata_riscv = { + .mnemonic = ".byte", + .callback = cap_skipdata_riscv_cb +}; + /* * Initialize the Capstone library. * @@ -76,6 +90,11 @@ static cs_err cap_disas_start(disassemble_info *info, csh *handle) cs_option(*handle, CS_OPT_SKIPDATA, CS_OPT_ON); switch (info->cap_arch) { + case CS_ARCH_RISCV: + cs_option(*handle, CS_OPT_SKIPDATA_SETUP, + (uintptr_t)&cap_skipdata_riscv); + break; + case CS_ARCH_SYSZ: cs_option(*handle, CS_OPT_SKIPDATA_SETUP, (uintptr_t)&cap_skipdata_s390x); diff --git a/include/disas/capstone.h b/include/disas/capstone.h index c43033f7f6..5c78824da2 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -11,6 +11,8 @@ /* Just enough to allow backends to init without ifdefs. */ +#define CS_API_MAJOR 0 + #define CS_ARCH_ARM -1 #define CS_ARCH_ARM64 -1 #define CS_ARCH_MIPS -1 @@ -37,4 +39,36 @@ #define CS_MODE_MIPS64 0 #endif /* CONFIG_CAPSTONE */ + +#if CS_API_MAJOR < 5 +#define CS_ARCH_RISCV -1 +#define CS_MODE_RISCV32 0 +#define CS_MODE_RISCV64 0 +#define CS_MODE_RISCV_C 0 +#elif CS_API_MAJOR == 5 +/* The C symbol name changed between v5 and v6 */ +#define CS_MODE_RISCV_C CS_MODE_RISCVC +#endif +#if CS_API_MAJOR < 6 +#define CS_MODE_RISCV_FD 0 +#define CS_MODE_RISCV_V 0 +#define CS_MODE_RISCV_ZFINX 0 +#define CS_MODE_RISCV_ZCMP_ZCMT_ZCE 0 +#define CS_MODE_RISCV_ZICFISS 0 +#define CS_MODE_RISCV_E 0 +#define CS_MODE_RISCV_A 0 +#define CS_MODE_RISCV_COREV 0 +#define CS_MODE_RISCV_THEAD 0 +#define CS_MODE_RISCV_SIFIVE 0 +#define CS_MODE_RISCV_BITMANIP 0 +#define CS_MODE_RISCV_ZBA 0 +#define CS_MODE_RISCV_ZBB 0 +#define CS_MODE_RISCV_ZBC 0 +#define CS_MODE_RISCV_ZBKB 0 +#define CS_MODE_RISCV_ZBKC 0 +#define CS_MODE_RISCV_ZBKX 0 +#define CS_MODE_RISCV_ZBS 0 +#define CS_MODE_RISCV_VENTANA 0 +#endif + #endif /* QEMU_CAPSTONE_H */ diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 23b5023dd3..e73ff53159 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -39,6 +39,7 @@ #include "system/tcg.h" #include "kvm/kvm_riscv.h" #include "tcg/tcg-cpu.h" +#include "disas/capstone.h" #if !defined(CONFIG_USER_ONLY) #include "target/riscv/tcg/debug.h" #endif @@ -1099,6 +1100,7 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info) { const RISCVCPU *cpu = RISCV_CPU(s); const CPURISCVState *env = &cpu->env; + int cap_mode; info->target_info = &cpu->cfg; @@ -1111,16 +1113,93 @@ static void riscv_cpu_disas_set_info(const CPUState *s, disassemble_info *info) switch (env->xl) { case MXL_RV32: info->print_insn = print_insn_riscv32; + cap_mode = CS_MODE_RISCV32; break; case MXL_RV64: info->print_insn = print_insn_riscv64; + cap_mode = CS_MODE_RISCV64; break; case MXL_RV128: info->print_insn = print_insn_riscv128; - break; + /* capstone v6 doesn't support RV128 */ + return; default: g_assert_not_reached(); } + + info->cap_arch = CS_ARCH_RISCV; + info->cap_insn_unit = 4; + info->cap_insn_split = 4; + + /* + * Capstone compresses some features together. See RISCV_getFeatureBits, + * which maps LLVM feature bits to capstone bits. + */ + if (riscv_has_ext(env, RVC) || cpu->cfg.ext_zca) { + cap_mode |= CS_MODE_RISCV_C; + } + if (riscv_has_ext(env, RVF)) { + cap_mode |= CS_MODE_RISCV_FD; + } + if (riscv_has_ext(env, RVV)) { + cap_mode |= CS_MODE_RISCV_V; + } + if (cpu->cfg.ext_zfinx || cpu->cfg.ext_zdinx || cpu->cfg.ext_zhinx) { + cap_mode |= CS_MODE_RISCV_ZFINX; + } + if (cpu->cfg.ext_zcmp || cpu->cfg.ext_zcmt || cpu->cfg.ext_zce) { + cap_mode |= CS_MODE_RISCV_ZCMP_ZCMT_ZCE; + } + if (cpu->cfg.ext_zicfiss) { + cap_mode |= CS_MODE_RISCV_ZICFISS; + } + if (riscv_has_ext(env, RVE)) { + cap_mode |= CS_MODE_RISCV_E; + } + if (riscv_has_ext(env, RVA)) { + cap_mode |= CS_MODE_RISCV_A; + } + if (cpu->cfg.ext_xlrbr) { + cap_mode |= CS_MODE_RISCV_BITMANIP; + } + if (cpu->cfg.ext_zba) { + cap_mode |= CS_MODE_RISCV_ZBA; + } + if (cpu->cfg.ext_zbb) { + cap_mode |= CS_MODE_RISCV_ZBB; + } + if (cpu->cfg.ext_zbc) { + cap_mode |= CS_MODE_RISCV_ZBC; + } + if (cpu->cfg.ext_zbkb) { + cap_mode |= CS_MODE_RISCV_ZBKB; + } + if (cpu->cfg.ext_zbkc) { + cap_mode |= CS_MODE_RISCV_ZBKC; + } + if (cpu->cfg.ext_zbkx) { + cap_mode |= CS_MODE_RISCV_ZBKX; + } + if (cpu->cfg.ext_zbs) { + cap_mode |= CS_MODE_RISCV_ZBS; + } + if (cpu->cfg.ext_xtheadba || + cpu->cfg.ext_xtheadbb || + cpu->cfg.ext_xtheadbs || + cpu->cfg.ext_xtheadcmo || + cpu->cfg.ext_xtheadcondmov || + cpu->cfg.ext_xtheadfmemidx || + cpu->cfg.ext_xtheadfmv || + cpu->cfg.ext_xtheadmac || + cpu->cfg.ext_xtheadmemidx || + cpu->cfg.ext_xtheadmempair || + cpu->cfg.ext_xtheadsync) { + cap_mode |= CS_MODE_RISCV_THEAD; + } + if (cpu->cfg.ext_XVentanaCondOps) { + cap_mode |= CS_MODE_RISCV_VENTANA; + } + info->cap_mode = cap_mode; } #ifndef CONFIG_USER_ONLY From 2b386072864da25299991c4ca3d30efdd9466376 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 12:47:26 -0700 Subject: [PATCH 616/860] target/sh4: Enable disassembly via capstone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/disas/capstone.h | 7 +++++++ target/sh4/cpu.c | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/disas/capstone.h b/include/disas/capstone.h index 5c78824da2..788f90771a 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -71,4 +71,11 @@ #define CS_MODE_RISCV_VENTANA 0 #endif +#if CS_API_MAJOR < 5 +#define CS_ARCH_SH -1 +#define CS_MODE_SHFPU 0 +#define CS_MODE_SH4 0 +#define CS_MODE_SH4A 0 +#endif + #endif /* QEMU_CAPSTONE_H */ diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index ad2ec28c1b..3bbdee301d 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -28,6 +28,7 @@ #include "fpu/softfloat-helpers.h" #include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" +#include "disas/capstone.h" static void superh_cpu_set_pc(CPUState *cs, vaddr value) { @@ -167,10 +168,23 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) static void superh_cpu_disas_set_info(const CPUState *cpu, disassemble_info *info) { + const CPUSH4State *env = cpu_env((CPUState *)cpu); + info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; info->mach = bfd_mach_sh4; info->print_insn = print_insn_sh; + + info->cap_arch = CS_ARCH_SH; + info->cap_insn_unit = 2; + info->cap_insn_split = 2; + /* + * Possible capstone bug: the isa levels are not additive: + * least significant bit wins, so SH4 overrides SH4A. + * Work around by setting one or the other but not both. + */ + info->cap_mode = CS_MODE_SHFPU + | (env->features & SH_FEATURE_SH4A ? CS_MODE_SH4A : CS_MODE_SH4); } static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) From 3a002bbcdd65030bc93f0435f91325f4ef5cb171 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 13:19:25 -0700 Subject: [PATCH 617/860] target/tricore: Enable disassembly via capstone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/disas/capstone.h | 11 +++++++++++ target/tricore/cpu.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/include/disas/capstone.h b/include/disas/capstone.h index 788f90771a..db1abe7f70 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -78,4 +78,15 @@ #define CS_MODE_SH4A 0 #endif +#if CS_API_MAJOR < 5 +#define CS_ARCH_TRICORE -1 +#define CS_MODE_TRICORE_110 0 +#define CS_MODE_TRICORE_120 0 +#define CS_MODE_TRICORE_130 0 +#define CS_MODE_TRICORE_131 0 +#define CS_MODE_TRICORE_160 0 +#define CS_MODE_TRICORE_161 0 +#define CS_MODE_TRICORE_162 0 +#endif + #endif /* QEMU_CAPSTONE_H */ diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 96e2817dee..dcd5c5065b 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -24,6 +24,7 @@ #include "qemu/error-report.h" #include "tcg/debug-assert.h" #include "accel/tcg/cpu-ops.h" +#include "disas/capstone.h" static inline void set_feature(CPUTriCoreState *env, int feature) { @@ -35,6 +36,35 @@ static const gchar *tricore_gdb_arch_name(CPUState *cs) return "tricore"; } +static void tricore_disas_set_info(const CPUState *cpu, disassemble_info *info) +{ + CPUTriCoreState *env = cpu_env((CPUState *)cpu); + + info->endian = BFD_ENDIAN_LITTLE; + info->cap_arch = CS_ARCH_TRICORE; + info->cap_insn_unit = 4; + info->cap_insn_split = 4; + + /* + * Possible capstone bug: the isa levels are not additive. + * Work around by settiing only one. + * Note that TriCore 1.3.0 is the earliest we support. + */ + if (tricore_has_feature(env, TRICORE_FEATURE_162)) { + info->cap_mode = CS_MODE_TRICORE_162; + } else if (tricore_has_feature(env, TRICORE_FEATURE_161)) { + info->cap_mode = CS_MODE_TRICORE_161; + } else if (tricore_has_feature(env, TRICORE_FEATURE_16)) { + info->cap_mode = CS_MODE_TRICORE_160; + } else if (tricore_has_feature(env, TRICORE_FEATURE_131)) { + info->cap_mode = CS_MODE_TRICORE_131; + } else if (tricore_has_feature(env, TRICORE_FEATURE_13)) { + info->cap_mode = CS_MODE_TRICORE_130; + } else { + g_assert_not_reached(); + } +} + static void tricore_cpu_set_pc(CPUState *cs, vaddr value) { cpu_env(cs)->PC = value & ~1; @@ -214,6 +244,7 @@ static void tricore_cpu_class_init(ObjectClass *c, const void *data) cc->gdb_write_register = tricore_cpu_gdb_write_register; cc->gdb_num_core_regs = 44; cc->gdb_arch_name = tricore_gdb_arch_name; + cc->disas_set_info = tricore_disas_set_info; cc->dump_state = tricore_cpu_dump_state; cc->set_pc = tricore_cpu_set_pc; From 724452279fda64259b480db07818bb0f11b0b5a7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 14:20:46 -0700 Subject: [PATCH 618/860] target/loongarch: Enable disassembly via capstone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Song Gao <17746591750@163.com> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/disas/capstone.h | 6 ++++++ target/loongarch/cpu.c | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/include/disas/capstone.h b/include/disas/capstone.h index db1abe7f70..a922f7627f 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -40,6 +40,12 @@ #endif /* CONFIG_CAPSTONE */ +#if CS_API_MAJOR < 6 +#define CS_ARCH_LOONGARCH -1 +#define CS_MODE_LOONGARCH32 0 +#define CS_MODE_LOONGARCH64 0 +#endif + #if CS_API_MAJOR < 5 #define CS_ARCH_RISCV -1 #define CS_MODE_RISCV32 0 diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index cb07f15110..8cd5bc18a3 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -29,6 +29,7 @@ #include #endif #include "tcg/tcg_loongarch.h" +#include "disas/capstone.h" const char * const regnames[32] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", @@ -694,8 +695,15 @@ static void loongarch_cpu_reset_hold(Object *obj, ResetType type) static void loongarch_cpu_disas_set_info(const CPUState *cs, disassemble_info *info) { + CPULoongArchState *env = cpu_env((CPUState *)cs); + info->endian = BFD_ENDIAN_LITTLE; info->print_insn = print_insn_loongarch; + + info->cap_arch = CS_ARCH_LOONGARCH; + info->cap_insn_unit = 4; + info->cap_insn_split = 4; + info->cap_mode = is_la64(env) ? CS_MODE_LOONGARCH64 : CS_MODE_LOONGARCH32; } static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) From c8220a096f00b21cad99d39e9c4736c1d1e81694 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 14:50:28 -0700 Subject: [PATCH 619/860] target/s390x: Update capstone disassembly to v6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In particular, this enables many more vector insns. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- disas/capstone.c | 2 +- include/disas/capstone.h | 11 +++++++++-- target/s390x/cpu.c | 4 +++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/disas/capstone.c b/disas/capstone.c index 78062339d0..a5bd91890b 100644 --- a/disas/capstone.c +++ b/disas/capstone.c @@ -95,7 +95,7 @@ static cs_err cap_disas_start(disassemble_info *info, csh *handle) (uintptr_t)&cap_skipdata_riscv); break; - case CS_ARCH_SYSZ: + case CS_ARCH_SYSTEMZ: cs_option(*handle, CS_OPT_SKIPDATA_SETUP, (uintptr_t)&cap_skipdata_s390x); break; diff --git a/include/disas/capstone.h b/include/disas/capstone.h index a922f7627f..87f8f6181f 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -4,7 +4,6 @@ #ifdef CONFIG_CAPSTONE #define CAPSTONE_AARCH64_COMPAT_HEADER -#define CAPSTONE_SYSTEMZ_COMPAT_HEADER #include #else @@ -19,7 +18,6 @@ #define CS_ARCH_X86 -1 #define CS_ARCH_PPC -1 #define CS_ARCH_SPARC -1 -#define CS_ARCH_SYSZ -1 #define CS_MODE_LITTLE_ENDIAN 0 #define CS_MODE_BIG_ENDIAN 0 @@ -84,6 +82,15 @@ #define CS_MODE_SH4A 0 #endif +#if CS_API_MAJOR == 0 +#define CS_ARCH_SYSTEMZ -1 +#elif CS_API_MAJOR < 6 +#define CS_ARCH_SYSTEMZ CS_ARCH_SYSZ +#endif +#if CS_API_MAJOR < 6 +#define CS_MODE_SYSTEMZ_ARCH14 0 +#endif + #if CS_API_MAJOR < 5 #define CS_ARCH_TRICORE -1 #define CS_MODE_TRICORE_110 0 diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 641ea96c8e..7c725b8a4a 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -225,10 +225,12 @@ static void s390_cpu_reset_hold(Object *obj, ResetType type) static void s390_cpu_disas_set_info(const CPUState *cpu, disassemble_info *info) { info->mach = bfd_mach_s390_64; - info->cap_arch = CS_ARCH_SYSZ; + info->cap_arch = CS_ARCH_SYSTEMZ; info->endian = BFD_ENDIAN_BIG; info->cap_insn_unit = 2; info->cap_insn_split = 6; + /* Disassemble everything, even if the cpu would trap on the insn. */ + info->cap_mode = CS_MODE_SYSTEMZ_ARCH14; } static void s390_cpu_realizefn(DeviceState *dev, Error **errp) From 89faaa51812765f1a8be59abe704bc88cfbdd17b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 16:12:43 -0700 Subject: [PATCH 620/860] target/m68k: Enable disassembly via capstone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/disas/capstone.h | 20 +++++++++++++ target/m68k/cpu.c | 65 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/include/disas/capstone.h b/include/disas/capstone.h index 87f8f6181f..3d6f90d816 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -14,6 +14,7 @@ #define CS_ARCH_ARM -1 #define CS_ARCH_ARM64 -1 +#define CS_ARCH_M68K -1 #define CS_ARCH_MIPS -1 #define CS_ARCH_X86 -1 #define CS_ARCH_PPC -1 @@ -28,6 +29,12 @@ #define CS_MODE_THUMB 0 #define CS_MODE_MCLASS 0 #define CS_MODE_V8 0 +#define CS_MODE_M68K_000 0 +#define CS_MODE_M68K_010 0 +#define CS_MODE_M68K_020 0 +#define CS_MODE_M68K_030 0 +#define CS_MODE_M68K_040 0 +#define CS_MODE_M68K_060 0 #define CS_MODE_MICRO 0 #define CS_MODE_MIPS3 0 #define CS_MODE_MIPS32R6 0 @@ -44,6 +51,19 @@ #define CS_MODE_LOONGARCH64 0 #endif +#if CS_API_MAJOR < 6 +#define CS_MODE_M68K_CF_ISA_A 0 +#define CS_MODE_M68K_CF_ISA_A_PLUS 0 +#define CS_MODE_M68K_CF_ISA_B 0 +#define CS_MODE_M68K_CF_ISA_C 0 +#define CS_MODE_M68K_CF_USP 0 +#define CS_MODE_M68K_CF_DIV 0 +#define CS_MODE_M68K_CF_MAC 0 +#define CS_MODE_M68K_CF_EMAC 0 +#define CS_MODE_M68K_CF_EMAC_B 0 +#define CS_MODE_M68K_CF_FPU 0 +#endif + #if CS_API_MAJOR < 5 #define CS_ARCH_RISCV -1 #define CS_MODE_RISCV32 0 diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index ce2707dee5..6012dc3186 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -22,7 +22,7 @@ #include "accel/tcg/cpu-ops.h" #include "fpu/softfloat.h" #include "qapi/error.h" - +#include "disas/capstone.h" #ifndef CONFIG_USER_ONLY #include "migration/vmstate.h" #include "monitor/hmp.h" @@ -182,9 +182,72 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) static void m68k_cpu_disas_set_info(const CPUState *cs, disassemble_info *info) { + CPUM68KState *env = cpu_env((CPUState *)cs); + int cap_mode = 0; + info->print_insn = print_insn_m68k; info->endian = BFD_ENDIAN_BIG; info->mach = 0; + + /* m68k support in capstone prior to v6 is unusably bad */ + if (CS_API_MAJOR < 6) { + return; + } + + info->cap_arch = CS_ARCH_M68K; + info->cap_insn_unit = 2; + + if (m68k_feature(env, M68K_FEATURE_M68K)) { + /* + * m68k insns may have up to 11 words, and 5 words are common. + * Choosing 12 bytes splits after 6 words and disassembly + * uses no more than 2 lines. + */ + info->cap_insn_split = 12; + if (m68k_feature(env, M68K_FEATURE_M68060)) { + cap_mode = CS_MODE_M68K_060; + } else if (m68k_feature(env, M68K_FEATURE_M68040)) { + cap_mode = CS_MODE_M68K_040; + } else if (m68k_feature(env, M68K_FEATURE_M68030)) { + cap_mode = CS_MODE_M68K_030; + } else if (m68k_feature(env, M68K_FEATURE_M68020)) { + cap_mode = CS_MODE_M68K_020; + } else if (m68k_feature(env, M68K_FEATURE_M68010)) { + cap_mode = CS_MODE_M68K_010; + } else { + cap_mode = CS_MODE_M68K_000; + } + } else { + /* ColdFire insns may have up to 3 words. */ + info->cap_insn_split = 6; + if (m68k_feature(env, M68K_FEATURE_CF_ISA_A)) { + cap_mode |= CS_MODE_M68K_CF_ISA_A; + cap_mode |= CS_MODE_M68K_CF_DIV; + } + if (m68k_feature(env, M68K_FEATURE_CF_ISA_B)) { + cap_mode |= CS_MODE_M68K_CF_ISA_B; + } + if (m68k_feature(env, M68K_FEATURE_CF_ISA_APLUSC)) { + cap_mode |= CS_MODE_M68K_CF_ISA_A_PLUS; + cap_mode |= CS_MODE_M68K_CF_ISA_C; + } + if (m68k_feature(env, M68K_FEATURE_USP)) { + cap_mode |= CS_MODE_M68K_CF_USP; + } + if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { + cap_mode |= CS_MODE_M68K_CF_FPU; + } + if (m68k_feature(env, M68K_FEATURE_CF_MAC)) { + cap_mode |= CS_MODE_M68K_CF_MAC; + } + if (m68k_feature(env, M68K_FEATURE_CF_EMAC)) { + cap_mode |= CS_MODE_M68K_CF_EMAC; + } + if (m68k_feature(env, M68K_FEATURE_CF_EMAC_B)) { + cap_mode |= CS_MODE_M68K_CF_EMAC_B; + } + } + info->cap_mode = cap_mode; } /* CPU models */ From 84dd090703e4164e209e0729333cfd30d8f9c9f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 8 Aug 2026 17:28:46 -0700 Subject: [PATCH 621/860] target/mips: Enable disassembly via capstone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/disas/capstone.h | 24 ++++++++++++++-- target/mips/cpu.c | 60 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/include/disas/capstone.h b/include/disas/capstone.h index 3d6f90d816..e72faf8179 100644 --- a/include/disas/capstone.h +++ b/include/disas/capstone.h @@ -29,6 +29,7 @@ #define CS_MODE_THUMB 0 #define CS_MODE_MCLASS 0 #define CS_MODE_V8 0 +#define CS_MODE_V9 0 #define CS_MODE_M68K_000 0 #define CS_MODE_M68K_010 0 #define CS_MODE_M68K_020 0 @@ -36,11 +37,10 @@ #define CS_MODE_M68K_040 0 #define CS_MODE_M68K_060 0 #define CS_MODE_MICRO 0 +#define CS_MODE_MIPS2 0 #define CS_MODE_MIPS3 0 -#define CS_MODE_MIPS32R6 0 -#define CS_MODE_MIPSGP64 0 -#define CS_MODE_V9 0 #define CS_MODE_MIPS32 0 +#define CS_MODE_MIPS32R6 0 #define CS_MODE_MIPS64 0 #endif /* CONFIG_CAPSTONE */ @@ -64,6 +64,24 @@ #define CS_MODE_M68K_CF_FPU 0 #endif +#if CS_API_MAJOR < 6 +#define CS_MODE_MIPS16 0 +#define CS_MODE_MIPS1 0 +#define CS_MODE_MIPS32R2 0 +#define CS_MODE_MIPS32R3 0 +#define CS_MODE_MIPS32R5 0 +#define CS_MODE_MIPS4 0 +#define CS_MODE_MIPS5 0 +#define CS_MODE_MIPS64R2 0 +#define CS_MODE_MIPS64R3 0 +#define CS_MODE_MIPS64R5 0 +#define CS_MODE_MIPS64R6 0 +#define CS_MODE_OCTEON 0 +#define CS_MODE_OCTEONP 0 +#define CS_MODE_NANOMIPS 0 +#define CS_MODE_MIPS_PTR64 0 +#endif + #if CS_API_MAJOR < 5 #define CS_ARCH_RISCV -1 #define CS_MODE_RISCV32 0 diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 669c7d99bb..0fead20d65 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -30,6 +30,7 @@ #include "system/qtest.h" #include "hw/core/qdev-properties.h" #include "hw/core/qdev-clock.h" +#include "disas/capstone.h" #include "fpu_helper.h" #ifndef CONFIG_USER_ONLY #include "semihosting/semihost.h" @@ -488,15 +489,66 @@ static void mips_cpu_disas_set_info(const CPUState *cs, disassemble_info *info) { const MIPSCPU *cpu = MIPS_CPU(cs); const CPUMIPSState *env = &cpu->env; + bool is64 = env->hflags & MIPS_HFLAG_64; + int cap_mode = 0; - if (!(env->insn_flags & ISA_NANOMIPS32)) { + if (env->insn_flags & ISA_NANOMIPS32) { + info->print_insn = print_insn_nanomips; + info->endian = BFD_ENDIAN_LITTLE; + /* nanomips has 16, 32 and 48-bit insns */ + info->cap_insn_unit = 2; + info->cap_insn_split = 6; + cap_mode = CS_MODE_NANOMIPS; + } else { info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE; info->print_insn = TARGET_BIG_ENDIAN ? print_insn_big_mips : print_insn_little_mips; - } else { - info->print_insn = print_insn_nanomips; - info->endian = BFD_ENDIAN_LITTLE; + + if (env->hflags & MIPS_HFLAG_M16) { + cap_mode = (env->insn_flags & ASE_MICROMIPS + ? CS_MODE_MICRO : CS_MODE_MIPS16); + /* Beware unsupported capstone mode */ + if (cap_mode == 0) { + return; + } + info->cap_insn_unit = 2; + } else { + info->cap_insn_unit = 4; + } + info->cap_insn_split = 4; + + cap_mode |= is64 ? CS_MODE_MIPS64 : CS_MODE_MIPS32; + if (env->insn_flags & ISA_MIPS_R6) { + cap_mode |= is64 ? CS_MODE_MIPS64R6 : CS_MODE_MIPS32R6; + } else if (env->insn_flags & ISA_MIPS_R5) { + cap_mode |= is64 ? CS_MODE_MIPS64R5 : CS_MODE_MIPS32R5; + } else if (env->insn_flags & ISA_MIPS_R3) { + cap_mode |= is64 ? CS_MODE_MIPS64R3 : CS_MODE_MIPS32R3; + } else if (env->insn_flags & ISA_MIPS_R2) { + cap_mode |= is64 ? CS_MODE_MIPS64R2 : CS_MODE_MIPS32R2; + } else if (env->insn_flags & ISA_MIPS5) { + cap_mode |= CS_MODE_MIPS5; + } else if (env->insn_flags & ISA_MIPS4) { + cap_mode |= CS_MODE_MIPS4; + } else if (env->insn_flags & ISA_MIPS3) { + cap_mode |= CS_MODE_MIPS3; + } else if (env->insn_flags & ISA_MIPS2) { + cap_mode |= CS_MODE_MIPS2; + } else if (env->insn_flags & ISA_MIPS1) { + cap_mode |= CS_MODE_MIPS1; + } + if (env->insn_flags & INSN_OCTEON) { + cap_mode |= CS_MODE_OCTEON | CS_MODE_OCTEONP; + } + if (is64 && !(env->hflags & MIPS_HFLAG_AWRAP)) { + cap_mode |= CS_MODE_MIPS_PTR64; + } + } + /* Beware unsupported capstone mode */ + if (cap_mode) { + info->cap_arch = CS_ARCH_MIPS; + info->cap_mode = cap_mode; } } From c54c041ae09004866ae049594ba9fb3a3c5ac271 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Fri, 22 May 2026 13:55:55 +0530 Subject: [PATCH 622/860] tests/qtest/libqtest: Use GLib functions for proper const correctness While commit e68da5b7a2cd ("tests/qtest: fix discarded const qualifier warning") addressed the immediate strstr() warning by making 'found' const, there's still a room for improvement: getenv() returns char *, but environment strings are semantically read-only and should be treated as const throughout their lifetime. Replace getenv() with g_getenv() and strstr() with g_strstr_len() to maintain const correctness from source to use. This approach: - Uses g_getenv() which returns const gchar *, matching the read-only semantics of environment variables - Employs g_strstr_len() for consistent use of GLib string functions, aligning with QEMU conventions - Eliminates all const-correctness warnings with strict compilers Tested-by: Anushree Mathur Reviewed-by: Aditya Gupta Signed-off-by: Amit Machhiwal Signed-off-by: Fabiano Rosas --- tests/qtest/libqtest.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index c33c799c92..bec37c71b8 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -2151,8 +2151,7 @@ bool mkimg(const char *file, const char *fmt, unsigned size_mb) bool qtest_verbose(const char *domain) { - const char *log = getenv("QTEST_LOG"); - const char *found; + const gchar *found, *log = g_getenv("QTEST_LOG"); assert(domain); @@ -2178,11 +2177,11 @@ bool qtest_verbose(const char *domain) * QTEST_LOG=,- (only false for domain2) * allows other separators, except - and + */ - found = strstr(log, domain); + found = g_strstr_len(log, -1, domain); if (found) { /* reject options given twice */ - assert(!strstr(found + strlen(domain), domain)); + assert(!g_strstr_len(found + strlen(domain), -1, domain)); if (found > log) { ptrdiff_t i = found - log - 1; @@ -2196,7 +2195,7 @@ bool qtest_verbose(const char *domain) * If filtering out a specific domain, all others are * enabled. */ - return !!strstr(log, "-"); + return !!g_strstr_len(log, -1, "-"); } } From 6500a6abe656743bab51cbff79a6b2c229cd688f Mon Sep 17 00:00:00 2001 From: Rohitashv Kumar Date: Fri, 17 Jul 2026 08:18:34 +0000 Subject: [PATCH 623/860] tests/qtest/device-plug-test: skip pc tests when 'pc' machine is unavailable test_pci_unplug_request() and test_pci_unplug_json_request() use "-machine pc" on i386/x86_64. When QEMU is built without the i440fx/pc machine, these fail with "unsupported machine type 'pc'". Skip the x86 case when 'pc' is not available. Non-x86 architectures use the default machine and are unaffected, and x86 unplug coverage is still provided by the q35 variant (test_q35_pci_unplug_request), which already guards on qtest_has_machine("q35"). Signed-off-by: Rohitashv Kumar Signed-off-by: Fabiano Rosas --- tests/qtest/device-plug-test.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index 2707ee59f6..650956fa11 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -65,6 +65,10 @@ static void test_pci_unplug_request(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -107,6 +111,10 @@ static void test_pci_unplug_json_request(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } From 4e37f749ddf4717b4f9df786232584f8a7953cb8 Mon Sep 17 00:00:00 2001 From: Rohitashv Kumar Date: Fri, 17 Jul 2026 08:18:35 +0000 Subject: [PATCH 624/860] tests/qtest/drive_del-test: skip pc tests when 'pc' machine is unavailable test_cli_device_del(), test_device_add_and_del(), test_drive_add_device_add_and_del() and test_blockdev_add_device_add_and_del() use "-machine pc" on i386/x86_64. When QEMU is built without the i440fx/pc machine, these fail with "unsupported machine type 'pc'". Skip the x86 case when 'pc' is not available. Non-x86 architectures use the default machine and are unaffected, and the corresponding _q35 variants already cover x86 under qtest_has_machine("q35"). Signed-off-by: Rohitashv Kumar Signed-off-by: Fabiano Rosas --- tests/qtest/drive_del-test.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 30d9451ddd..4c32da594a 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -258,6 +258,10 @@ static void test_cli_device_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -332,6 +336,10 @@ static void test_device_add_and_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -403,6 +411,10 @@ static void test_drive_add_device_add_and_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } @@ -456,6 +468,10 @@ static void test_blockdev_add_device_add_and_del(void) } if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (!qtest_has_machine("pc")) { + g_test_skip("Machine 'pc' is not available"); + return; + } machine_addition = "-machine pc"; } From bb98963d6df300fe6fc6d2074198e220571cd0cd Mon Sep 17 00:00:00 2001 From: Rohitashv Kumar Date: Fri, 17 Jul 2026 08:18:36 +0000 Subject: [PATCH 625/860] tests/qtest/pxe-test: skip per-row cases whose machine is unavailable The x86 test table mixes 'pc' and 'q35' rows. When QEMU is built without the i440fx/pc machine, the 'pc' rows fail with "unsupported machine type". Add a per-row qtest_has_machine() check in test_batch(), mirroring the existing per-row qtest_has_device() guard, so rows for an unavailable machine are skipped while the others (e.g. q35) still run. Signed-off-by: Rohitashv Kumar Signed-off-by: Fabiano Rosas --- tests/qtest/pxe-test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qtest/pxe-test.c b/tests/qtest/pxe-test.c index a3f900fbea..e85dec5a4e 100644 --- a/tests/qtest/pxe-test.c +++ b/tests/qtest/pxe-test.c @@ -108,6 +108,10 @@ static void test_batch(const testdef_t *tests, bool ipv6) const testdef_t *test = &tests[i]; char *testname; + if (!qtest_has_machine(test->machine)) { + continue; + } + if (!qtest_has_device(test->model)) { continue; } From 03ce28d45362dfaab99dd3a6ac842c617c548530 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 22 Jul 2026 11:11:34 +0200 Subject: [PATCH 626/860] target/i386: emulate: stop torturing cc_src into carrying SF/PF x86_flags.c encodes the flags into cc_dst and cc_src with algorithms essentially derived from Bochs; the exact details have changed but cc_dst is Bochs result and cc_src is very close to Bochs auxbits. However, using only two words is unnecessarily limiting because it splits SF/PF between the two words even though *ZF* is the real nuisance (ZF=1 implies SF=PF=0) and the one that commands usage of PD/SD delta bits. Within TCG, the CCMP instruction would have a similar need of efficiently encoding an arithmetic result or an EFLAGS value; it is not implemented, but there are plans (see commit message for 5dcdbd07125, "target/i386: tcg: use cout to commonize add/adc/sub/sbb cases", 2025-04-17) to use an algorithm very similar to target/i386/emulate's, but with *three* words. Then SF and PF live together in harmony, because SF can be encoded with either parity and PF does not use the high bit where SF is stored; by placing them in a third word their computation is isolated from ZF's and everything becomes simpler. In fact I'm not even sure why Bochs did it like that, and did not just give SF/PF their own home in a third word as well; the developers believe that the extra store is too expensive. I am not really sure about that, but as far as QEMU is concerned, emulation proceeds one instruction at a time so using SRC2 should actually be faster, not just easier. To convert from the output of arithmetic operations, PD and SD disappear and DST simply has to be stored in two places; to convert to RFLAGS, SF/PF are easily computed from SRC2 as if PD=SD=0; conversion to LFLAGS encodes parity in bit 0 and mixes in SF as an even-parity value with the right sign bit. Unlike TCG, there is a single meaning for all operand lengths, which corresponds to either CCMPL or CCMPQ depending on sizeof(target_ulong). So the carry-out value still needs to be split---with AF in bit 3 and CF/PO in the higher bits of the target_ulong-sized env->cc_src. This is an acceptable tradeoff for the interpreter, in order to optimize lflags_to_rflags. Signed-off-by: Paolo Bonzini --- target/i386/emulate/x86_flags.c | 65 +++++++++++++-------------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/target/i386/emulate/x86_flags.c b/target/i386/emulate/x86_flags.c index 3c4270a14c..c49d8bb836 100644 --- a/target/i386/emulate/x86_flags.c +++ b/target/i386/emulate/x86_flags.c @@ -30,28 +30,24 @@ /* - * The algorithms here are similar to those in Bochs. After an ALU - * operation, CC_DST can be used to compute ZF, SF and PF, whereas - * CC_SRC is used to compute AF, CF and OF. In reality, SF and PF are the - * XOR of the value computed from CC_DST and the value found in bits 7 and 2 - * of CC_SRC; this way the same logic can be used to compute the flags - * both before and after an ALU operation. + * The emulator always encodes flags in the same way as CC_OP_CCMPB + MO_TL. + * While for arithmetic operations ZF/SF/PF are computed from the same value, + * ZF=1 may be inconsistent with PF/SF for arbitrary RFLAGS values so CC_SRC2 + * is used for SF and PF. CC_SRC holds a carry-out vector that is used to + * compute AF, CF and OF. * * Compared to the TCG CC_OP codes, this avoids conditionals when converting * to and from the RFLAGS representation. + * + * The underlying ideas ultimately descend from Bochs, but with significant + * simplifications obtained by storing flags in three words rather than two. */ #define LF_SIGN_BIT (TARGET_LONG_BITS - 1) -#define LF_BIT_PD (2) /* lazy Parity Delta, same bit as PF */ -#define LF_BIT_AF (3) /* lazy Adjust flag */ -#define LF_BIT_SD (7) /* lazy Sign Flag Delta, same bit as SF */ #define LF_BIT_CF (TARGET_LONG_BITS - 1) /* lazy Carry Flag */ #define LF_BIT_PO (TARGET_LONG_BITS - 2) /* lazy Partial Overflow = CF ^ OF */ -#define LF_MASK_PD ((target_ulong)0x01 << LF_BIT_PD) -#define LF_MASK_AF ((target_ulong)0x01 << LF_BIT_AF) -#define LF_MASK_SD ((target_ulong)0x01 << LF_BIT_SD) #define LF_MASK_CF ((target_ulong)0x01 << LF_BIT_CF) #define LF_MASK_PO ((target_ulong)0x01 << LF_BIT_PO) @@ -59,19 +55,15 @@ /* OSZAPC */ /* ******************* */ -/* use carries to fill in AF, PO and CF, while ensuring PD and SD are clear. - * for full-word operations just clear PD and SD; for smaller operand - * sizes only keep AF in the low byte and shift the carries left to - * place PO and CF in the top two bits. +/* + * For arithmetic operations ZF/SF/PF are consistent so DST == SRC2. + * For operations that are not full-word, keep AF in the low byte and shift + * the carries left to place PO and CF in the top two bits. */ #define SET_FLAGS_OSZAPC_SIZE(size, lf_carries, lf_result) { \ - env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \ - target_ulong temp = (lf_carries); \ - if ((size) == TARGET_LONG_BITS) { \ - temp = temp & ~(LF_MASK_PD | LF_MASK_SD); \ - } else { \ - temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \ - } \ + env->cc_dst = env->cc_src2 = (target_ulong)(int##size##_t)(lf_result); \ + target_ulong temp = (lf_carries) & MAKE_64BIT_MASK(0, size); \ + temp |= temp << (TARGET_LONG_BITS - (size)); \ env->cc_src = temp; \ } @@ -93,13 +85,9 @@ /* same as setting OSZAPC, but preserve CF and flip PO if the old value of CF * did not match the high bit of lf_carries. */ #define SET_FLAGS_OSZAP_SIZE(size, lf_carries, lf_result) { \ - env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \ - target_ulong temp = (lf_carries); \ - if ((size) == TARGET_LONG_BITS) { \ - temp = (temp & ~(LF_MASK_PD | LF_MASK_SD)); \ - } else { \ - temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \ - } \ + env->cc_dst = env->cc_src2 = (target_ulong)(int##size##_t)(lf_result); \ + target_ulong temp = (lf_carries) & MAKE_64BIT_MASK(0, size); \ + temp |= temp << (TARGET_LONG_BITS - (size)); \ target_ulong cf_changed = ((target_long)(env->cc_src ^ temp)) < 0; \ env->cc_src = temp ^ (cf_changed * (LF_MASK_PO | LF_MASK_CF)); \ } @@ -255,7 +243,7 @@ void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2, static inline uint32_t get_PF(CPUX86State *env) { - return ((parity8(env->cc_dst) - 1) ^ env->cc_src) & CC_P; + return (parity8(env->cc_src2) - 1) & CC_P; } static inline uint32_t get_OF(CPUX86State *env) @@ -283,8 +271,7 @@ static inline uint32_t get_ZF(CPUX86State *env) static inline uint32_t get_SF(CPUX86State *env) { - return ((env->cc_dst >> (LF_SIGN_BIT - LF_BIT_SD)) ^ - env->cc_src) & CC_S; + return (target_long)env->cc_src2 < 0 ? CC_S : 0; } void lflags_to_rflags(CPUX86State *env) @@ -304,16 +291,14 @@ void rflags_to_lflags(CPUX86State *env) { target_ulong cf_af, cf_xor_of; - /* Leave the low byte zero so that parity is always even... */ - env->cc_dst = !(env->eflags & CC_Z) << 8; - - /* ... and therefore cc_src always uses opposite polarity. */ - env->cc_src = CC_P; - env->cc_src ^= env->eflags & (CC_S | CC_P); + /* compute DST and SRC2 that reconstruct ZF/SF/PF. */ + env->cc_dst = ~env->eflags & CC_Z; /* DST = 0 if ZF=1 */ + env->cc_src2 = ~env->eflags & CC_P; /* odd parity if PF=0 */ + env->cc_src2 ^= -!!(env->eflags & CC_S); /* rotate right by one to move CF and AF into the carry-out positions */ cf_af = env->eflags & (CC_C | CC_A); - env->cc_src |= ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1))); + env->cc_src = ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1))); cf_xor_of = ((env->eflags & (CC_C | CC_O)) + (CC_O - CC_C)) & CC_O; env->cc_src |= -cf_xor_of & LF_MASK_PO; From 7c84752396d16c3379091fa8341c902aa67ec3d3 Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Wed, 12 Aug 2026 10:28:09 +0200 Subject: [PATCH 627/860] whpx: i386: fix xsaves enablement in legacy probing path Without this, Linux will crash in a configuration where xsaves is expected. Signed-off-by: Mohamed Mediouni Link: https://lore.kernel.org/r/20260812082814.27217-2-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini --- target/i386/whpx/whpx-cpu-legacy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/whpx/whpx-cpu-legacy.c b/target/i386/whpx/whpx-cpu-legacy.c index d341e6f4fd..3fb5ed79e3 100644 --- a/target/i386/whpx/whpx-cpu-legacy.c +++ b/target/i386/whpx/whpx-cpu-legacy.c @@ -113,7 +113,7 @@ uint32_t whpx_get_supported_cpuid_legacy(uint32_t func, uint32_t idx, if (idx == 0) { eax = supported_xcr0; } else if (idx == 1) { - eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1; + eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES; if (!whpx_has_xsaves()) { eax &= ~CPUID_XSAVE_XSAVES; } From 32452bac6d991e9778dd7a9fb2b3eed3a2b23d71 Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Wed, 12 Aug 2026 10:28:10 +0200 Subject: [PATCH 628/860] hw/i386: fw_cfg: do not set VMX feature control on WHPX Hyper-V doesn't allow setting this bit on build 26100. Signed-off-by: Mohamed Mediouni Link: https://lore.kernel.org/r/20260812082814.27217-3-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini --- hw/i386/fw_cfg.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index d422302c1c..5903994a7c 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -13,6 +13,8 @@ */ #include "qemu/osdep.h" +#include "system/mshv.h" +#include "system/whpx.h" #include "system/numa.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" @@ -182,8 +184,15 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg) uint64_t *val; cpu_x86_cpuid(env, 1, 0, &unused, &unused, &ecx, &edx); - if (ecx & CPUID_EXT_VMX) { - feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX; + + /* + * Hyper-V in 26100 disallows this bit to be set. + * Otherwise a #GP gets raised. + */ + if (!(whpx_enabled())) { + if (ecx & CPUID_EXT_VMX) { + feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX; + } } if ((edx & (CPUID_EXT2_MCE | CPUID_EXT2_MCA)) == From 3bfcd8ef6bd26cb80175f40f60e67bc6f42fa0a4 Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Wed, 12 Aug 2026 10:28:11 +0200 Subject: [PATCH 629/860] whpx: i386: enable fast hypercall output Already enabled on Arm so extending it to x86. Signed-off-by: Mohamed Mediouni Link: https://lore.kernel.org/r/20260812082814.27217-4-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini --- target/i386/whpx/whpx-all.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 634d542821..1800ba8eb1 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -3247,6 +3247,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms) synthetic_features.Bank0.HypervisorPresent = 1; synthetic_features.Bank0.Hv1 = 1; + synthetic_features.Bank0.FastHypercallOutput = 1; synthetic_features.Bank0.AccessVpRunTimeReg = 1; synthetic_features.Bank0.AccessPartitionReferenceCounter = 1; synthetic_features.Bank0.AccessPartitionReferenceTsc = 1; From 7b0516912e44e73ba0859e91aed848d5be26f05e Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Wed, 12 Aug 2026 10:28:12 +0200 Subject: [PATCH 630/860] whpx: i386: synchronise PAT too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/cmspam/winq-emu hints that this might be wanted for some use-cases and it's a step towards full state sync: > Linux uses PAT to mark virtio-gpu / Venus shared memory as > Write-Combining. Previously the partition's PAT was not > synchronised with the guest, so the guest's MTRR/PAT cache-type > computation could fall back to UC for memory that should be WC. Signed-off-by: Mohamed Mediouni Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20260812082814.27217-5-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini --- target/i386/whpx/whpx-all.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 1800ba8eb1..2739a80da3 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -115,7 +115,7 @@ static const WHV_REGISTER_NAME whpx_register_names[] = { #ifdef TARGET_X86_64 WHvX64RegisterKernelGsBase, #endif - /* WHvX64RegisterPat, */ + WHvX64RegisterPat, WHvX64RegisterSysenterCs, WHvX64RegisterSysenterEip, WHvX64RegisterSysenterEsp, @@ -624,9 +624,8 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level) assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase); vcxt.values[idx++].Reg64 = env->kernelgsbase; #endif - - /* WHvX64RegisterPat - Skipped */ - + assert(whpx_register_names[idx] == WHvX64RegisterPat); + vcxt.values[idx++].Reg64 = env->pat; assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs); vcxt.values[idx++].Reg64 = env->sysenter_cs; assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip); @@ -962,9 +961,8 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level) assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase); env->kernelgsbase = vcxt.values[idx++].Reg64; #endif - - /* WHvX64RegisterPat - Skipped */ - + assert(whpx_register_names[idx] == WHvX64RegisterPat); + env->pat = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs); env->sysenter_cs = vcxt.values[idx++].Reg64; assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip); From 37ce6d8da75754f725db8cc27ae6c82a2e5b930e Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Wed, 12 Aug 2026 10:28:13 +0200 Subject: [PATCH 631/860] whpx: i386: work around Hyper-V FP state oddities On Hyper-V looks like we need to fetch both the legacy and new state instead of being able to rely on xsave. Signed-off-by: Mohamed Mediouni Link: https://lore.kernel.org/r/20260812082814.27217-6-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini --- target/i386/whpx/whpx-all.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 2739a80da3..56a8b16749 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -614,9 +614,9 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level) if (whpx_is_xsave_enabled(cpu)) { whpx_set_xsave_state(cpu); - } else { - whpx_set_legacy_fp_registers(cpu, level); } + whpx_set_legacy_fp_registers(cpu, level); + /* MSRs */ assert(whpx_register_names[idx] == WHvX64RegisterEfer); vcxt.values[idx++].Reg64 = env->efer; @@ -950,9 +950,8 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level) if (whpx_is_xsave_enabled(cpu)) { whpx_get_xsave_state(cpu); - } else { - whpx_get_legacy_fp_registers(cpu, level); } + whpx_get_legacy_fp_registers(cpu, level); /* MSRs */ assert(whpx_register_names[idx] == WHvX64RegisterEfer); From 93f38b6e8817280e1a51102d2dfd21115b8d4e88 Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Wed, 12 Aug 2026 10:28:14 +0200 Subject: [PATCH 632/860] whpx: i386: inject back db When we get a debug exception other than int1, inject it back to the guest. Signed-off-by: Mohamed Mediouni Link: https://lore.kernel.org/r/20260812082814.27217-7-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini --- target/i386/whpx/whpx-all.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 56a8b16749..113f79afb9 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1079,6 +1079,31 @@ static void whpx_inject_back_gpf(CPUState *cpu) whpx_set_reg(cpu, WHvRegisterPendingEvent, reg); } +static void whpx_inject_back_db(CPUState *cpu) +{ + WHV_VP_EXCEPTION_CONTEXT *ctx = &cpu->accel->exit_ctx.VpException; + WHV_REGISTER_VALUE reg = {}; + + if (ctx->ExceptionInfo.SoftwareException) { + /* TODO */ + warn_report("Was asked to inject software exception."); + return; + } + + if (ctx->ExceptionType != EXCP01_DB) { + warn_report("Was asked to inject exception other than debug."); + return; + } + + reg.ExceptionEvent.EventPending = 1; + reg.ExceptionEvent.EventType = WHvX64PendingEventException; + reg.ExceptionEvent.DeliverErrorCode = ctx->ExceptionInfo.ErrorCodeValid; + reg.ExceptionEvent.Vector = ctx->ExceptionType; + reg.ExceptionEvent.ErrorCode = ctx->ErrorCode; + reg.ExceptionEvent.ExceptionParameter = ctx->ExceptionParameter; + whpx_set_reg(cpu, WHvRegisterPendingEvent, reg); +} + static void handle_io(CPUState *env, uint16_t port, void *buffer, int direction, int size, int count) { @@ -2646,11 +2671,7 @@ int whpx_vcpu_run(CPUState *cpu) } else if ((vcpu->exit_ctx.VpException.ExceptionType == WHvX64ExceptionTypeDebugTrapOrFault) && !cpu_single_stepping(cpu)) { - /* - * Just finished stepping over a breakpoint, but the - * gdb does not expect us to do single-stepping. - * Don't do anything special. - */ + whpx_inject_back_db(cpu); cpu->exception_index = EXCP_INTERRUPT; } else { /* Another exception or debug event. Report it to GDB. */ From eb3f7fe382babbab44419603e81da56abb91d11a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Aug 2026 16:23:54 +0200 Subject: [PATCH 633/860] serial: clear transmit retry callback on unrealize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GSource is removed when resetting but remains active (and can cause use-after-free) on hot-unplug. Remove it before the character device is disconnected. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4125 Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- hw/char/serial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/char/serial.c b/hw/char/serial.c index 0729cd2ce9..4339562ab0 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -936,6 +936,7 @@ static void serial_unrealize(DeviceState *dev) { SerialState *s = SERIAL(dev); + g_clear_handle_id(&s->watch_tag, g_source_remove); qemu_chr_fe_deinit(&s->chr, false); timer_free(s->modem_status_poll); From 501187d0bc62a46628acc25e0ea0187094f04246 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Aug 2026 15:14:07 +0200 Subject: [PATCH 634/860] meson: make linker warnings non-fatal on Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These cause a werror=true build to fail with Meson 1.12.0, as it now makes linker warnings fatal as well. Cc: qemu-stable@nongnu.org Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build index 49a5baf5b5..40e3f4ec44 100644 --- a/meson.build +++ b/meson.build @@ -602,6 +602,11 @@ if host_os == 'windows' qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-seh', '-Wl,--nxcompat') qemu_ldflags += cc.get_supported_link_arguments('-Wl,--dynamicbase', '-Wl,--high-entropy-va') endif +if host_os == 'linux' and get_option('werror') and get_option('prefer_static') + # On glibc systems, glib causes warnings about getpwuid, getpwuid_r + # and getpwnam_r not being supported with static linking + qemu_ldflags += cc.get_supported_link_arguments('-Wl,--no-fatal-warnings') +endif if get_option('fuzzing') # Specify a filter to only instrument code that is directly related to From 2a428c8541ec5e9be54aad5f0a446ec8172226c9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Aug 2026 15:12:55 +0200 Subject: [PATCH 635/860] dockerfiles: remove packages required by Avocado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make our CI containers smaller, by removing a handful of packages that were only included as dependencies of Avocado. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/macos-14.vars | 2 +- scripts/ci/setup/debian/debian-13-ppc64le.yaml | 4 ---- scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml | 4 ---- scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml | 4 ---- tests/docker/dockerfiles/alpine.docker | 3 --- tests/docker/dockerfiles/centos9.docker | 3 --- tests/docker/dockerfiles/debian-amd64-cross.docker | 4 ---- tests/docker/dockerfiles/debian-arm64-cross.docker | 4 ---- tests/docker/dockerfiles/debian-armhf-cross.docker | 4 ---- tests/docker/dockerfiles/debian-i686-cross.docker | 4 ---- tests/docker/dockerfiles/debian-ppc64el-cross.docker | 4 ---- tests/docker/dockerfiles/debian-riscv64-cross.docker | 4 ---- tests/docker/dockerfiles/debian-s390x-cross.docker | 4 ---- tests/docker/dockerfiles/debian.docker | 4 ---- tests/docker/dockerfiles/fedora-rust-nightly.docker | 4 ---- tests/docker/dockerfiles/fedora-win64-cross.docker | 4 ---- tests/docker/dockerfiles/fedora.docker | 4 ---- tests/docker/dockerfiles/opensuse-leap.docker | 4 ---- tests/docker/dockerfiles/ubuntu2404.docker | 4 ---- tests/lcitool/projects/qemu.yml | 4 ---- tests/vm/generated/freebsd.json | 4 ---- 21 files changed, 1 insertion(+), 79 deletions(-) diff --git a/.gitlab-ci.d/macos-14.vars b/.gitlab-ci.d/macos-14.vars index def77cfdea..0c187624dd 100644 --- a/.gitlab-ci.d/macos-14.vars +++ b/.gitlab-ci.d/macos-14.vars @@ -12,5 +12,5 @@ NINJA='/opt/homebrew/bin/ninja' PACKAGING_COMMAND='brew' PIP3='/opt/homebrew/bin/pip3' PKGS='bash bc bindgen bison bzip2 capstone ccache cmocka coreutils ctags curl dbus diffutils dtc flex gcovr gettext git glib gnu-sed gnutls gtk+3 gtk-vnc jemalloc jpeg-turbo json-c libcbor libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb llvm lzo make meson mtools ncurses nettle ninja pixman pkg-config python-setuptools python3 rpm2cpio rust sdl2 sdl2_image snappy socat sparse spice-protocol swtpm tesseract usbredir vde vte3 vulkan-tools xorriso zlib zstd' -PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme tomli' +PYPI_PKGS='sphinx sphinx-rtd-theme tomli' PYTHON='/opt/homebrew/bin/python3' diff --git a/scripts/ci/setup/debian/debian-13-ppc64le.yaml b/scripts/ci/setup/debian/debian-13-ppc64le.yaml index e64321357d..9acd99a743 100644 --- a/scripts/ci/setup/debian/debian-13-ppc64le.yaml +++ b/scripts/ci/setup/debian/debian-13-ppc64le.yaml @@ -105,9 +105,6 @@ packages: - openssh-client - pkgconf - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - python3-setuptools - python3-sphinx @@ -115,7 +112,6 @@ packages: - python3-tomli - python3-venv - python3-wheel - - python3-yaml - rpm2cpio - rustc - sed diff --git a/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml b/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml index 12b0e38a22..5c948109df 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2404-aarch64.yaml @@ -106,9 +106,6 @@ packages: - openssh-client - pkgconf - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - python3-setuptools - python3-sphinx @@ -116,7 +113,6 @@ packages: - python3-tomli - python3-venv - python3-wheel - - python3-yaml - rpm2cpio - rustc-1.83 - sed diff --git a/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml b/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml index 56299f4cc2..010b7f86aa 100644 --- a/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml +++ b/scripts/ci/setup/ubuntu/ubuntu-2404-s390x.yaml @@ -104,9 +104,6 @@ packages: - openssh-client - pkgconf - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - python3-setuptools - python3-sphinx @@ -114,7 +111,6 @@ packages: - python3-tomli - python3-venv - python3-wheel - - python3-yaml - rpm2cpio - rustc-1.83 - sed diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 118c6e8eea..8a5a355e89 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -83,14 +83,11 @@ RUN apk update && \ pixman-dev \ pkgconf \ pulseaudio-dev \ - py3-numpy \ - py3-pillow \ py3-pip \ py3-setuptools \ py3-sphinx \ py3-sphinx_rtd_theme \ py3-wheel \ - py3-yaml \ python3 \ rpm2cpio \ rust \ diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index 0d5b5fa7f5..e38cd692db 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -96,9 +96,6 @@ RUN dnf --quiet distro-sync -y && \ pkgconfig \ pulseaudio-libs-devel \ python3 \ - python3-PyYAML \ - python3-numpy \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 061cd8e9b8..823606dc52 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 3dfa60fdfd..ed58e089ec 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 401c64269c..0843f7428a 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 8b60c7b085..caba08f842 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 2a7e8cba05..17018c0dab 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 349de3a0bb..2408d453c2 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 8bc42b1e00..2b0a344c2c 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -41,9 +41,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -51,7 +48,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 413986d3f0..064b2ad1f1 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -113,9 +113,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -123,7 +120,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc \ sed \ diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index 4f84b80379..dcad3af2a7 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -104,10 +104,6 @@ exec "$@"\n' > /usr/bin/nosync && \ pkgconfig \ pulseaudio-libs-devel \ python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index c1e63feb46..bce350dfd8 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -44,10 +44,6 @@ exec "$@"\n' > /usr/bin/nosync && \ ninja-build \ openssh-clients \ python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index a6996917c4..c541f5d5b1 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -104,10 +104,6 @@ exec "$@"\n' > /usr/bin/nosync && \ pkgconfig \ pulseaudio-libs-devel \ python3 \ - python3-PyYAML \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index fc363c647c..8d8631bbcf 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -93,12 +93,8 @@ RUN zypper update -y && \ pcre2-devel-static \ pipewire-devel \ pkgconfig \ - python3-Pillow \ - python3-PyYAML \ python3-Sphinx \ python3-base \ - python3-numpy \ - python3-opencv \ python3-pip \ python3-setuptools \ python3-sphinx_rtd_theme \ diff --git a/tests/docker/dockerfiles/ubuntu2404.docker b/tests/docker/dockerfiles/ubuntu2404.docker index 27992e34fd..f027eef022 100644 --- a/tests/docker/dockerfiles/ubuntu2404.docker +++ b/tests/docker/dockerfiles/ubuntu2404.docker @@ -113,9 +113,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ openssh-client \ pkgconf \ python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ python3-pip \ python3-setuptools \ python3-sphinx \ @@ -123,7 +120,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-tomli \ python3-venv \ python3-wheel \ - python3-yaml \ rpm2cpio \ rustc-1.83 \ sed \ diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 7d185cda3c..b3c9993672 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -91,11 +91,7 @@ packages: - pkg-config - pulseaudio - python3 - - python3-numpy - - python3-opencv - - python3-pillow - python3-pip - - python3-PyYAML - python3-setuptools - python3-sphinx - python3-sphinx-rtd-theme diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index 08b6eb6155..dfa8b41a52 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -49,14 +49,10 @@ "ncurses", "nettle", "ninja", - "opencv", "pixman", "pkgconf", "png", - "py311-numpy", - "py311-pillow", "py311-pip", - "py311-pyyaml", "py311-setuptools", "py311-sphinx", "py311-sphinx_rtd_theme", From 51ea699eb84677490312bb09b3c1e9cfb645152c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Aug 2026 15:09:16 +0200 Subject: [PATCH 636/860] dockerfiles: update CentOS Stream 9 to Python 3.11, Meson to 1.12 Python 3.10, which is a requirement for Meson 1.12.0, was never packaged for CentOS Stream 9 so update directly to 3.11. CentOS 9 does not install meson from PyPI because configure will install it from the bundled wheel; this way CI covers that path as well. This is guaranteed because, when using a non-default Python, basically nothing is installed. The opensuse-leap-15.yml file was not in use anymore, so delete it while touching tests/lcitool/targets/. Signed-off-by: Paolo Bonzini --- tests/docker/dockerfiles/centos9.docker | 16 +++++++----- .../dockerfiles/debian-all-test-cross.docker | 2 +- .../dockerfiles/debian-amd64-cross.docker | 2 +- .../dockerfiles/debian-arm64-cross.docker | 2 +- .../dockerfiles/debian-armhf-cross.docker | 2 +- .../dockerfiles/debian-i686-cross.docker | 2 +- .../dockerfiles/debian-ppc64el-cross.docker | 2 +- .../dockerfiles/debian-riscv64-cross.docker | 2 +- .../dockerfiles/debian-s390x-cross.docker | 2 +- tests/docker/dockerfiles/debian.docker | 2 +- .../dockerfiles/fedora-rust-nightly.docker | 2 +- .../dockerfiles/fedora-win64-cross.docker | 2 +- tests/docker/dockerfiles/fedora.docker | 2 +- tests/docker/dockerfiles/ubuntu2404.docker | 2 +- tests/lcitool/mappings.yml | 26 ++++++++++++++++++- ...ensuse-leap-15.yml => centos-stream-9.yml} | 0 16 files changed, 47 insertions(+), 21 deletions(-) rename tests/lcitool/targets/{opensuse-leap-15.yml => centos-stream-9.yml} (100%) diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index e38cd692db..8101370a2d 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -95,13 +95,11 @@ RUN dnf --quiet distro-sync -y && \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ - python3 \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx_rtd_theme \ python3-tomli \ - python3-wheel \ + python3.11 \ + python3.11-pip \ + python3.11-setuptools \ + python3.11-wheel \ rdma-core-devel \ rust \ rust-std-static \ @@ -131,11 +129,15 @@ RUN dnf --quiet distro-sync -y && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc +RUN /usr/bin/pip3.11 install \ + sphinx \ + sphinx-rtd-theme + ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" ENV MAKE="/usr/bin/make" ENV NINJA="/usr/bin/ninja" -ENV PYTHON="/usr/bin/python3" +ENV PYTHON="/usr/bin/python3.11" # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 2dc580552e..833185f607 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -47,7 +47,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 823606dc52..9e30457ca7 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -66,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index ed58e089ec..b9b94e5e6f 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -66,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 0843f7428a..aa412ff66f 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -66,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index caba08f842..d07eac84fe 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -66,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 17018c0dab..7302c8236e 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -66,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 2408d453c2..aa5a881cab 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -66,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 2b0a344c2c..82eae4fc2c 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -66,7 +66,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 064b2ad1f1..3a4f208ccf 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -145,7 +145,7 @@ 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 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index dcad3af2a7..0f3dc370a4 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -145,7 +145,7 @@ 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 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index bce350dfd8..6f972f6173 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -66,7 +66,7 @@ exec "$@"\n' > /usr/bin/nosync && \ nosync dnf --quiet clean all -y && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install meson==1.8.1 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index c541f5d5b1..99bb38a366 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -145,7 +145,7 @@ 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 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/docker/dockerfiles/ubuntu2404.docker b/tests/docker/dockerfiles/ubuntu2404.docker index f027eef022..a5c22231f9 100644 --- a/tests/docker/dockerfiles/ubuntu2404.docker +++ b/tests/docker/dockerfiles/ubuntu2404.docker @@ -145,7 +145,7 @@ 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 +RUN /usr/bin/pip3 install meson==1.12.0 ENV CCACHE_WRAPPERSDIR="/usr/libexec/ccache-wrappers" ENV LANG="en_US.UTF-8" diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 62fe60d047..1816556dde 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -3,6 +3,30 @@ mappings: bindgen: Ubuntu2204: + python3: + CentOSStream9: python3.11 + + python3-devel: + CentOSStream9: python3.11-devel + + python3-pip: + CentOSStream9: python3.11-pip + + python3-setuptools: + CentOSStream9: python3.11-setuptools + + python3-sphinx: + CentOSStream9: + + python3-sphinx-rtd-theme: + CentOSStream9: + + python3-venv: + CentOSStream9: python3.11 + + python3-wheel: + CentOSStream9: python3.11-wheel + meson: # Use Meson from PyPI wherever Rust is enabled Debian: @@ -23,7 +47,7 @@ mappings: pypi_mappings: # Request more recent version meson: - default: meson==1.8.1 + default: meson==1.12.0 # Drop packages that need devel headers python3-tomli: diff --git a/tests/lcitool/targets/opensuse-leap-15.yml b/tests/lcitool/targets/centos-stream-9.yml similarity index 100% rename from tests/lcitool/targets/opensuse-leap-15.yml rename to tests/lcitool/targets/centos-stream-9.yml From 4c8c1320367a5b84864700d4634a8f07aed4753a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Aug 2026 10:27:10 +0200 Subject: [PATCH 637/860] python, meson: update meson required for Rust to 1.12.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Meson 1.11.0 brings support for parsing Cargo.toml inside QEMU's source tree, and Meson 1.12.0 brings support for cross-compilation of Cargo subprojects. Together, these two features allow QEMU to remove hundreds of lines of manual compilation scripts. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- configure | 4 ++-- python/scripts/vendor.py | 4 ++-- python/wheels/meson-1.11.1-py3-none-any.whl | Bin 1078534 -> 0 bytes python/wheels/meson-1.12.0-py3-none-any.whl | Bin 0 -> 1106208 bytes pythondeps.toml | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 python/wheels/meson-1.11.1-py3-none-any.whl create mode 100644 python/wheels/meson-1.12.0-py3-none-any.whl diff --git a/configure b/configure index 6ddfe4b534..7f637c018c 100755 --- a/configure +++ b/configure @@ -1166,12 +1166,12 @@ fi # detect rust triple meson_version=$($meson --version) -if test "$rust" != disabled && ! version_ge "$meson_version" 1.10.0; then +if test "$rust" != disabled && ! version_ge "$meson_version" 1.12.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.10.0, disabling" 2>&1 + echo "Rust needs Meson 1.12.0, disabling" 2>&1 rust=disabled fi fi diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index 1bb59d863a..9b4c763f9e 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py @@ -41,8 +41,8 @@ def main() -> int: parser.parse_args() packages = { - "meson==1.11.1": - "9b3a023657e393dbc5335b95c561337d49b7a458f5541e47ec44f2cc566e0d80", + "meson==1.12.0": + "71f133147fa0fcfe8f4df49fa1045771064947834538409e5d97b3613aac8b4e", "qemu.qmp==0.0.6": "5d7c5af0e9de427696e3bf72e333965c3a697929f77f6b7ddc30c989fc7b539b", "pycotap==1.3.1": diff --git a/python/wheels/meson-1.11.1-py3-none-any.whl b/python/wheels/meson-1.11.1-py3-none-any.whl deleted file mode 100644 index 8dc3c9f2f6db1215bd9d64eaec84dce03fd79f1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1078534 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 z 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 z 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 }CqMz%l!3(_IbZSh4Gm|+z8Iec}q3i5_{HJ)@ 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

XI9GjL)=>OGA8OUZSe3j20jK{Zv)z%534 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ⅇ|F>l zC2{9n$o73%1R=&V9&=2k(KNJ~z8L8H+Guyof%z6Yy3yLPk&UPZx~G9?uDoS_G8U{T zZ{>Y9a2$3t5#zEm3rLZ?3voLhih4WQ0a_*s_@wB*4~UGL$Ik=7f`FN=mQ$L$qL7y+ zhtUZ?cn+&Hu->_n8IF;;bJr>eu17+?$9!A$mTTO9VgoGgz zJ9n%q88G~z#^gLD#R0mpL~}#nc3CK__~o&Ia@)+Aq)mTZ3?3#FSnRwiz&|35tDuFSUs-# z5FEBAChIBPQ6feFvPuNA!Q@zL^ic^Mb2?X+Fi{ludqD2#$IB^27n=5n~NsCrw{T03>!rDoM-7@yB zWnNOgdnXi$y4p<4Y>?t=!EiUgE{(|k*4JQ02hm~~J`{@&DOHvp0WJvxjS>1+2GHuc zR&r>~H&F@gpA3Ln8$Z~wDj84)>!Q*`Bz+R@cL%DPof(zS2Upqr<8)qB@Ne@myBXP+ z7N%8-V|Jnh7p9xMTvnQ9JSn9y#e0X+%;w+)LW0uo@cW5`+Mg|H%PQHrD{rZ9<7B6; z+D=)!bv4Hd1GEJM^Vphu%l2f)3Lxb?)<-;d-dW(J=&+P31(y8@uHzYpslEtIE13w3 z{A9Go3k;ut%ec;%LN|gBpd)TdVUdi=zztVx6^wbqNy%mA^R~_}FSD8~BZOgu25Len zs%mY8TSgOct0lAzKcYvZXK_rZi38|i+fwSRS?(_3Ic#T%jT%s({EGU4(8mtnC@2#$ zSJ}s&J4$cEFm;hPA8AyKNDSJ!%0%QT=xFQI6yD;DXEf|i(Z_q!%r_V{XH{)VjI*7Q zmta~tl^-XMA3uR*1QW1mlIX-VyT7@*^|C4QK>o`0x?EYEi;89Z45=|fY9s?%)RHA+ zi4ol)u09)NB#7sZ8ahn=ZTY#eDM{UJc>ch!L9i;XPHzR(&+cK7+@sTQ>_poEx0^Hc zF`KpCy`~T9e%D_x+OKI@HEYNHst9Us)^=Ech16m)bhQz5nJ34)`=+!?%%eo$;FnTh zz%o5Sf*q^Oy-3=W`@>pq2CgkMpA*!JK=a#2D{$ZE6)cL_vOX{K*{w>`BCb!h1Q(wr zLezeq&s&+PqJu|<_|V0Lu`*;rxvv3L9|91D<#dcLu@rrs;>_Z~cusZI?J$Wc@QPJZ+>Us5b@WPt51WG1xaDtRXCB_zTHC^MtKHX}l`id_O$Ql)-t9(e zUFJBqc~+1$sxZTGSa7#>a7ce{S&sFz<*6?OciX`}s=nOS=!jcKh7=fSXtdPE{YPuH z>5W+WDJGo;3>|^OhIMG@jfRg|!-r=#%#t%@7iPN*bX3)WsXa-0qHgJkI@NWKVcmeW z=**Kc^&)Wt_wixKh?6+{$!f)yUAS$(6_>}feH)xx8m;t5OG`8Bx@@*Dou-@bQTX+b zmLUTgvIGfa3=CZsx}BiYSU{UGYrcF2R2&c|rp9h;_Uq1s!A?0Cw1bb(OrVq$x}V%) z^c#%pLz87%*6m2aZQo>t=R*!G6xhTg`7Twa`u@xm7gQPyakE@4Re}s*P>1T&<}V^8 zLL=M;6fkSqlg!?5&TE_?5e3P_9+`+@u^%U}NH2lip2xi@13) ziJWnV&Rv0>#&XTs&W_FgZsEq;d<9T*wPmVg2c@u;+mrj{w=n)^8y)Oq{ciVtS09Hk z+C0QhN{}2h&4s zD6As8vU2PXjP1&J6;_!B47$YO%pc~(2IbhqT%JZ>02J!IA_`zIV7wAL6Yj?>gVR`s zp>oR%M876A#YxFjL#COgE7PE(R0)_O19`40NJpMigXvtIe6te5NF4C`Xq3XlZ95lq z#e)52D?76|!_Y0j?xBoxi#^>@PTzX8Lv1J(!3^58T4BjhPCs#s5KBymW<=BMk)6A- z|I-3v-QP+=l$6IFi*&aUy}>x2jpxW8$f4-$v)&z|b1wY~f^;|(tz&u| zBGGMfcF=by51J>6=FmXcsbiB3XI0;82}7?2E!GaoA+FRqy>ukXBkPxY6ffsnB4+1Zp|Y{6MtXLg7Eb5n zUG0v@R^1f`xBx1Dwm`}R=+u< z0gKX{{E*(xWGo9TdEF=}1>j<=KBbb8;SH<*)JhkY@65olrVOUa@k3F}WC$<@hbeXP z9=Qb@tJ|9kdoKOX^~tAJU8}=8Io+ud?a02ko@GWHH1OwgD98r)%79uSF9o0u9Lr7B zMexJu1=^*`HYUU{jm^IsXp-{Y9x453zhbI%VDET&_2p@og`A(Z=vyxeSIRhRBLZgU`O(i4U zU{p~m4MHL$mJ$o^I_vX#nx0H=A??zj$^QqhQ zCMN$4PFnme9n*Gi$E-FnSanA$&)&a#_u}>0RCEuHh�ws_vM(7$Xg;9m03B@wd-k zy#4;<)r|{d zV6<{?TQxrwMq>P!k(|7yv1OlNE1gy!ov7qaCrRNlOf5aUx4k`e@o6DxLeRg)?ZU(8 z6({?qBpxVkfON{YtQcDjBG`FqfLptwj@pt3kUv% zZO+Y~1VV*644P8Y>1NindAkvEQ=PHz2g7u zbBvmao0&ANGYs}Qte^tYsIE+fixCAJ;O};B2z7-+#5F_ZoluYwZ=ig$xOc(aP+T%o zM@cLgU%otT;oS-&`YG$}gcP6~n82FDrsN>km}c!c+b_CFPX6R=fRWjx#C&8fMjh|&5 ziq{MreSY}!vLbac^85@0(RF4X`bZ@Pg|Jk(jrZk}Qs4l!u|}8%%0!W2^0Xr3@Pxpi zH*1lIskG*^Dkxeb2jIc<%4D!XBv#2BzlbToD`xQ0(9F3dClI_5q_Zv^YSUYqY>uj^ z?E_R=oR>`-n^V+e?Pi_8ZYf1>VCK+n9sTeQ<)t;}mzMGMNm#!m_G7?+Y0MU3Obw3d z^QYhc$yuT21~2{jXY}Gch{s_|0X8tqjacfdh39MSFa!$Wp|u|rmJU_-M*h_;*>R)^ zbzvDr7ru%}B-0R)=fY<_-|%nnM*9WZ*3Mg&HIx8r*XF9an61}?)g|IoEA^7HG7hQw z<49!EXs(LvQ-X{y8<`IJJqHoxYY;t(&STwjeBnAfZc2$ZfE1i~*bxoWA+>$H&<+16 zavB)($nm9w7=k~vsEWUcY<}6#wwz^v&z3aoB*#Jm0F` zxfu6B(?^O?e4zYENg_%mG-Vh=NP3tdEYr4^=lw1)4~N^eY~EFqKLH(3`kWX& z^c_aM*z*8(c{4`Xm6dSe~zi~&p*cz25`!Rr#;4QqC9=kT|NJ7`I?D< zJ0gz96#Z^BjTtWBx=WPqcZ#`G0$SCgsy=S$EhfH#C-v>fkCqEtB|(WT=Ai)xPsh9a zHLLlO`Bvj+6nB?IE+wvU8v9#rUMTb?!k>ZV2_Z~VfpxZU6H#`B%07GZ{0&|EU)9y- z@`^k?=0LhRdP_GLn(Sl7CYF9~J`>63>|H3iYI81-cX9ss!U(>)S*6X#FN}a9$$(&) z(GPrkcPcW8M&uI%>vL>}Kv6YOQZnf1OYCy)?-$)4OJuiduoG@;O-^2S5yswqM%pbh z>;TSs<~Z}WWBYZ z(4xz=!mPIkE=dg`u+ny^mm3@Qe}R49D|rtu|41%-C*Xn+AI5FP@a3miAl?4_W>hz8 zlNpOJBBr3bfkUM#)uxxy%OCeN?e7_i*apKU^PS#|hrHmsE(0YyP{mbFFpAm1uLI#4 zX=zeXGyE-ci9wYmLou@$&4*SN7wHawY4H`JVWF+O8w|_e2dJr@7+4C7I~=NLMgwVD z83)TMCpE_OkVs)n`foaFi41@}C|m54|7Glf>jK8b9cayM(`KtST@Qz&tEkr`LoTw}1|-PuS*%9g z9C9nkfH8Ivo;oO=m}tdg^g7X{6_Cc+JqL!;(|LNj+`((VUU3{| zQ89GKWX^a_!J)j;{M50!co?0gn6!Ie>Q%LQ3~Kj`>bAUGZdOy#tBL%9NgXnI+k|05 z@t0#=;hVu}K+@jq?uh~!SXb!q9-oP;Ie8y<_c>B#Mfh++x_5ZGXN+JEZEI^(s&2{! zmrH&wyKe0E-r`M4yhQ7ym}Xwt4WaVc*7>UyL|BX4dc$>oF84aX$J)q9oG~O24u_+> zw%8q5MuiMq7KSegF^9-1#e@zbXc;4P;s)xR*i$JdFBpoE=hKiC#u({B)DIFPg`1PX zK^++m-+9z1@9uFwi|8@|kJ^;YknWV=pjcNobQ{7Ur);uZLf_k|HYMWGxCR?$DH*fP zTPRq`>D-CrJK8dq@i^*DmoXrmtAsBYR51|;UmPeWsE#nW119k!O@0m1-YwA@8Lw1J zkpEgB)uIXIclrlr2W?=hFrZdT4=~?IsCyf6GvdPwv>?u3xKZQ%NIIwz-5*sSvyzen zynOvWZUh+80MN}@7JZXpdVp^tI4aC4ilUy1o(DjRv7oIn3>NQqaV?QCGdW9@aCX~J zo*AIDtMod@ON|>^$us|Qlp-S04T=~Y?zacDDT(ehiGA%O<}DCS*z99w=s1WF3TS^XquvFz7P4jU9+*}}k% z*Xtr9kf*-{sHQ`?au_|Mg9Y~I6vui%aQJK1&XedW2MUIHV9vFw0=Lc&#ouLKX8V`G zz)HYEA8q>mo5OLHaedV3o_tC`WOe~x-@}`+grXr5^*a|QjA4ZBsnO)em($G_CS>p0 z9kZ74B3aD3&R2YejJEH>Hh(0X;16_2>w^y|8b7oF^R9={^9ol%hVkj?PEs=0O6dI# z(jrfrtT~`;9R5Af(&nea-H_~cI}#J5mzLB4zk!Y7mElMTuwAUto+>_DDA74=Ss{XW z>fqBV8HI}Pxk&hE(rua{-2oh3nKALojThZfO?>R)%jWGkyHvKRNDdo!a+)_1&d|t@ z*ZnS#^-R@l9%=5)oNYlx3f%>4dU*u@PxO*HQr|jEmqefp^Y$=Ukc1(mT8?Xr1V=$b zaHa$`_a(2OBCfi|np7^*9Tl|Es?}3gUJ=z$kX`PV4X$;gt#*T6Btjjgun>MjUREtd!O0Cd?Es7=B0@tD}b>?gSg^t(6z_!{|Os7J%9Cz?Udy?g6vJC=bt_eY+r^C4FqKF>_T%iSoNeAx~qKO`DV z;M_goEL9W~+}>2~0o7)qmC!RE1;q%1*WC*>MmX$#dKkQ8;YX$wHw8|T^=X1hP#?^1 zbm>l)1JnO@Jn3kqe|TY<)G+QZ3dzrJM{xI95nn~_ksn2(Xp21gGMaMBXgN$yxTIfU2WvSR}LWpJOw0AFtrhyy^MsM}BJt`Ypuh+`yAk;Vp(zNQQyLr0*( zWg=X}m~Nm%NTVW@y`+k=G`uAI5UO22?c$ehb1m~_T6HaK8kITHIm&OS!}M$!U@ z&iai6RKD_a!~EV?D(&T`_!%NSbRVI)Vco3#Bm8!!n1;UAo7iNOVrk?jGd4iEDq?i0IF)9#sS2$6k z3-%;9SI+WQ`6_hr?JJusmKF9BjBMjn}Zg4}Qvxy>}u;Y}Q z!A-#H*i?h`!G;m|(oNKMW$vXQ6jd00MjFvJ@Y;FecX5#j^6h1WrY-QmRSs49RykS} zSab>x{M8K)ofSvf|KRaqcsf_xrW1s9n#yJkhp$nb#N+e*Cw}-2wYpZr7A(#7uoZ^+ z)f2YV>y|@Q`wnR{+K{Xz4B*!AFpeJK(87zQUr)Zhson-;wAN)}2UZOYmlAj>!wGTK zLz>HARSP`+RX&KzIJuKhsfPhY8Z1v(YoT?!J;3JHAfx^|YT96aHxXM}KAZsyAayI7 z#s=YVuLVHuc4^K%uPwheOQtK6D^O}_oWMcciIFxD-I&N;Ov39xGA2KRWKj58B+!pu zKI6F)3}iO4o(iEPZZ+}o^PDWx9nKluypV7rP7mcgR=^nv;d){)V4W_7ABQq@h`*6_;CN zoffmgNkpt;yWrI0P;{wR&&BP>LgdVrM`EZgb|ExIhE+`KeMk-y4bu+v6k-)q<`qGf zBYPCA*d;s}DCmR0Yhf{TF^HZH6OiQiw@F(QiGYbYpkXJ)g}dAq-g;w$6`cSVN~j;h z8gZpDfG|KDGqe8&*R<;*HE>x|gARW*3|0axQufkWj|E%WBD5{*u~4_Lx*dljT9@)g z=gOpe?f|V@H`Ybs6Dt#HF_2DWGY;zrTlf@-49F>~S?VtA&lVQmW!+;>x9z#MgJVLo z2zVyg=fsd>LbFIvE;E>TAVfbF*mVQIPe)pTE#SIBL?WvQ$gUFHpk)ff6}=UVb6_QD zoIfjM&2dtbbUVTjB=%G{!A$n|bchZ1W9l71)sEyz_S^)WjeF`dDVQttcSp_>g6~hK z(Q=@CNQ;G&K>oVQt|<&PfJE^)AYu#Z2n*Rk)X3+9fiZhA2DwEkY|Xxf8aTr&HmlWb z?0Uoi{8J(?%NB@xGl|sbb=M8Jytl!F>=S83=)1#|hR~V2lOc=f+sO>oC9|3EhGvG8 z$g5JvJ&;jVrujm6oZ3-V{OnAzRDB!p5L0nS&L){RDJ7o~GJqx~5Mfj>;rYN}`f-?e zUCHhQe=e{iO}}~KIeOAOS!_tH4Q<*k~R!#U}6>wfe|8X00vAh{*j}QMe!T? zN(X!8#h(uVR%L=fEtsFxFT*qEOx=SQ`6|bIBXono%lGJjV|`|LU5+Mi88Th+uHxWk zGG4QA<*Kf;y2e;%bjburx~dl8lv$2#f)EStml#oOMu?PYo!;ocKmui5h3$w@_(4+r zKq7z*G8nAX=>Lc9A!gmSA3@_ELTVN*2u#d?!pKQ|rJHNy@u4fYYN&aCXEob)Qhj|G z*7}#Fw%p!Bg$(GC7IcITs^aKXXk07ac4WG8h``T!F*ZW+=(3JCn~r_o8KvGQmDa+^ z7F(QlwVujWG+7JpGG8W?)N5Aj)Tr)GzUG*}68++5CmQzqFc|1)P^!p2V}7$?hrWh( zVn7_y?$-Pi?{;f_I_qHgQA`^Y?QxLN594G?^3Zhpc~2VLG@CVAX_LuThxm~g`^Ri^ z89UcT!iW6a-`iukx5tN3ETWzUEKg&J2PmfUIQXUXt_8H*>age0Ueev zD~eW7xu&o>!VA2^Ne)14DvrF5SR>=~*{v1x0CTY6i7X>;Uo;hkB}@@PC>=`xBE=QC z&WdUcJB*1ZU&_-&kpL-1m`fE+Wen@f!Fn;emNxmlnlCeqEQ zhhqOjSJg|Ic5GNo-66WMa55*BaX7uiQ51js=Ka~*_n7`*2uKkqU#I>g-2`f0F9?{9 zjXwk$VWrW)tW-u4=wQ2j3S6wmb@G~qMZ2{e{;?H3*gU|VacU#nQGS{m2^Jffc`)O+ z5}4BvR;G$(z$O_fd2s3&55h-Lq@3Phu!1wgI=uv0BBw(*pQ$-)H9V~`>4xmAf=0&m zjc#+ow-CUCYpez|pOt)w4-QQw6gk5<3bY(S?7S6ewJFx}DsQX>BP3HJ(~r=_&(bEJ ztHTJBs+*O|3U?(5#yx29o+|tH3IQtip~cN#Eh_yJQmpezSwp}VVX%GjU@XtiGG=L5N!~3Y#i?*44nK`Gs$(+ju@pMj%%ZtM-XZX(ic3hm4r6NA z*I&;j*m>Lx9o*qx(x<*z%6yeDe(Yw zK#RXHnjXJ;C1O&aPB8=Qwwi+PP=XEC+Ha}A?5?rGNLts^#!&HW-&_pc5SbGERP^D^ z?-bpe)P}dH(5pzO3glL%CcI-OQ?UAWG|n-l60C-|D-0KYq!lYKKEbjE#0Uk8%_uO2 zwHX$Y^9Hy@Tn_5)ZardHH}6&j0g8oaLAB<(8D=d3J>^sh(IeHx1iDmG&cvaa!p}`A zyinWbQT*-OSH~x>UmySQ;@e~~&?Q(9=#lvAg%VUqNMOD&$BPUT%WKFNb&W)N{fk_X zjB(G^OU-oC#*LyENHR~Gb`*OnIG#9n2^hY3Kxw5z@-Wa%K#F#>npY+*)t%ifh77?m z=hsYN#S^3uo;TAChB4g6LIZn7uZG*2mtgElX6*9eby@_HZpheC0(MMYwd1f;MG)kH z8wH!O_7t=Hav82*_sgiE?}M9gO?`JF%Ap~E!LmBS;-8evx}psAvxl&wLW}uAh9;xv zeYijG(dhRMqdR#2Id*P{ZH#TrwZg)Hbv#qibUE|N3vumN-|~weNd?$7{o+d9)8K#1 zZed<^pk7fTjb&841&A(LQ6((gLqE6GRC1)cUq zzH3Ry(AV*U{~u6G0|XQR000O8bh?yWiL~r&J;49~RWt(tA^-pYZDn(BZen$5Y-BHD zVPk7$Ze%ZRX>Mv^Vqs%zWo~3HaCy|d>w4SBl_>h(Pk~7NZICUBYCGwfL>Z-{$ac48 zd>cznGF#SWKoFGBCIL18T4FTX?{i+`JlR>7x>o^&WM^g$zobnOg;lpztJZz(MrZGy z|8@UWo@Paz?VrxFqRHoZRvkvit0cY7_MeTvd$Jpyl&ejZUtKrRv+tfh+lT-Bb98>4 zMX$5EETUQVIa`#gCA=IpS+cZV_<#Q&c<0%#;hksSN2f)TE#P6bDyyW)%i_tCc~vf> z$z;B6)>Sr{MEMe)HBnL&WkXGB(Izbyi!7yIWTRO&Pu7cOmZ!}qdRxu1Dw{orKlp`N z(j@63sq0L?Ab;W(S+QQqM=#*}C|Vc!FYAn-&euiSl;uLby{Oic6jz0xuaf3^kx%8* zcQF2U@Y5*5|8kQiyKQc&WTgj;EBr+Kl{Z;M9mpSkhSikcOZDq2Pd_a(`B$aB)$8f1 zD$}f1Et}0MFRr4bj?TrRjb+c%B>j{XGcob8`h8i>){D&l88;Rt=l(uj=Zl%wcu`)t zzoq4Jl`pc&gNzfNWlbh`X*^DPe+!H}%K$syWt9LhBH-_;@~VQZ`%?mg`ej`<*#y=>BH#oU z$*A))xI;kg!mXkVL(bBAv3J{Kd8%2w<{4~vA3~C3xm-8Nbdh1t zu(VAH)WU3Ujq}BAQza>k^X>F!z{6T?a&5jM(8xun=bIu~=BdP|GbB>tFY|4Mu|Dp; zN{Xv>a+S3-*Q;GiEDyJYjfZ&@viVE?v-_QDtqh5yQpxJ+#Dsf6M zx6ePkvS0of4dTO@-S@iAMn7Fq?nZT%9Y)tpv#Jjd4rXOqkKr`H7MAk{G3OvF_Sf~n4G@{~raqWu^{1v>9VDy# zpuWx)3;a^d_vQEfly?vgS=N-$=jWTslJNr7y>NHtpz#43#>=uv# z3Base!?9-yT8lTV2I_U-0#;R)W;41X`Hcw3qX03Zmth3BeAyUQeo)1Oj~~As?f|AN zlI3)kM9ae{{`fJDzKsTptQaiES5>)Q4W15%@YAzsFM2wJ|HSwPKpGn30R&v;MY6EQ zvw(}~Ca<%o$g&xaL!Oz?K)&fakfGE4%vvS`{yg5fgKzgcF|T%&0T))_!zaMR{Z!sW z1s*e4%`9ER0!L!IBHXV!n&tC361Hj9++?*78t4=@IRFA7}<*gnzXaamm* z0H}4c7+*Kb1?>8KQURtEK6EhK3f!&N3wYA|F> z1q=5hhz4*&0o9phQ4P=3SsgRR)i8Uwma^3%NwWds^awE<{vBE#3<8Vx5m6&JB3I2dEWt8wp5RJ0*T8#S10xE(D;z_v%6d4U zj`#$8PO2Okj5@lxF5$!iYqN$KU6*SBf)ooDYhBPNXJe9$xCIXfk2rz>a2n8{tW2<3 z1SpA(K_n?)%iy8}X{iDnX^=SNK)ag7CYo*(s6jTq8q;jezCg%WA$BR)2r40I1vriI&JLfEWhnJ0gM|iTORLlA8$v!`-`!pTBtb z@)Yj94{!ec!`t&0&v6T%zc@R=e^9kA` zTMnZsj2Ym(OyG>$=L|?v{IQ7XWZ>)cBc1fBTGmWaV`fc&ecHNF+4?f!(nVqreuZm5S6tMKJLA z8|%-34GeFM_)xVN>ZP>^fVvRB`FQm&-~9owhgw^t0)GLP?vGm7r~e9u^Le%cE;K9B z%~_fhAh4Vk^YV}Z?Ck8|U)7Qn4{&5^xFqBP1B%xCMwCp;HIhZfVnN9cLDou^z+>b< zlClXL*hAW1@k0dopET*UILT$T0fJc(WC_NA!UEm1k_42)=r~%!4T7W<&Nx4d=#29r z6eHx;HJxy^e00X)0H<)yk&v*g1ym-`A6zX-M6$T*rdB-@WfDCtgb2fUjK2cyQeDEI zxKc(hu&}&H*6P5q;yb{e?D-gVI2Uv>k*7D>)Q4IQIA5<88MjjWeYzroA|#{u@)CLb zKMJJ|W5K`k#Pxb8hXSy~w=s@{D9#(`LmvTre@r)}{nIZBfcW;r=o|VH zUE4IwI4KO5U;1NBlI${TuFIJMgUB#gs8z@9fC;Ff6J+tsS%yomH@C$g2T(&g1vzmj zmw`j8-T@NH5L597p^qBqO_d{WL<D$1PVy_GAmDWJO9M`Kw-VBh$ z8w3bH0_2#xiFc!T*ztVP^^l-H>3U{P{XHD8R*b^aCy+|;JBP01Op-wD|7;B~V)*KY z7BGPsLjJ+N5JyA^acx;!1ji1s0B0b%BdWDtMQyMh@+{=2Exc;hf1j0195#kLu{qySb+(m+Xo zhu)CJu(VZEDsaRC+#vKEF z8GpIWRy93`e~22ch{{LtRhq^mODyZpY3#j3Eb)=NaqP!*%fLhW*M21KIovey4Oe)s zXA>beJIw+Us^^Pzi%aqsT0K@6HO;Q{$-{BxNXgA01duT_Iv-mqJX zxj|yFr!nAiXak0W2%^;-7yV*MuP=#QW#f5X%#gb#9ppE6A8+>V{}X8H&ED{bi~sY@ z<+rGJcZL=|cl1G(9#e6kmC>(`qW!0r`uM<8dnaLz-w5S`a}QbV_Mn|sK6J6OYK;!v za}2EL9M8o`vREJrTnJuQup(Oh|4!>A6dlmFhoLb$C{7N7VWPy847~jhTjl7|Fn&y^id6G{2OR;-|co(OM6 zideIUOJm#Jn>RRUOMz^&!K8Ah11@B_Qxv7AELmTA46a4={5yY>XNy@B$KlsBMQnX@ z0vi+S5#j>CYf@t-2HGW>&0}mB-Knei{tf`#TQZjz6avOIr#}4=b)r=r#-K14@3k7XQ7W*$NtJ)UKXs+lT-X`OOx68pT^yKxl680qM=i;xv&~ zDRD#Q1->EmfC7N{vz2Me7TK~z?`de|E?Gg5o3s@;?>#xc@wD z1~hrS?&c9OWpx`HG#IqyuIiWt8~LKoIY_MU3lSUJK=GH$z&tKuxX`Jk*!yrJKm~3k zal+t=cTu^6)I>|mS{T%vfV6)eA@2F4#=26M>Rd_2-=3}lwtoctdREMkTi-WWcdK=J zy)HhXAvLT8LCp=exOjR=*X>>4u9YCT;j=>&3#{Uu(Q`K)Ng@IL`02-~<4N;A=Osee#Zjt_pb;tNsQYG?LGRB8{yi>{8Q9&nWvSh}J!2Y`1B0rwO_uf_ZPWYf#;fb(i*kO{3N@`U7d6wcDE} zEu^bquS{SPGdQ+eXrPS%ziL4M*)V{LK96T1gi@&QN#_jt2{VYs%hsEZR$t1Y zTeqBpq9IQ!|0RRq6kiW(zZ6^5m@HoIp`!~ih?h2nHQ_th;dT9=@c%vgclCyWJ5rq- zKr6HBR%|mkJR6Vqq*~H!jWJKaHcZ!7gL!;nctz7;x&!pPkIe;A;v@1KKpmp* z>@iRmdwOHSxN&RZ25SY&s>x;sXd5L|w~E&Z^lkF47i^7i)Q+mfW!s=1HEKO5W7G=h z$3m~*Zgh@nmAqz`JXEFt3l*IrF@+sLdm3Jz%j8qW3YeR+`ULHer8a=97Dk{ zh$+IWUdfYW1|LUfz-AxY4MLpo}+z`F*VnC4gO za$Tc)!6;gm@GHmbBt^5>j0~8wl5T9_QzBhTFoF%_=i2Z{i7>neqe=lF+l*1ql40Ot z!d{S;P737ZI@Roo0x~Td33#HK02>Q(n0Z?EtZw7&@tZ-Qh}r(Nq+m0be$> zM5a%+p#Wo;k{_oNOyqHMxSXZlS}^FY{KbT$m}K7i^1I`wP@q zn+a^oRHoI_BxzZ1T5LZ`95!pucNF`@9I+ta`Ur(kV;)C$Ot=9}Y!zj86A<0yQ0aCK zj8be-j}s$5Z6}Ak)i}&(Ux$kfKD;>ztJimH;Y2l?G9Sra%bj)uy z(KY-_{|R<3cTx&{7R&V@<6ak}U*-sG=p)&lL4YKHmS*O?ho2CY2tRYoCFymRev%5M zHUR_ixCnBt9?8+oOl@dbK5#Ww^Z508(d4TIy6%^=bxNU8&^LR5v$KA*b64E&j3X7M zK__@qt}D@?9@|Uf4u%vt#W3LUjA6OYvz5^LDs_$whlJ}kStyTxGTvCxKYydJUJS$j z8@+*12LJ)?Exp`LIl)u|dZA61dv?S3-M8K45c{`is>_}~eB8v@JX>*>~Y$n+0{L1pQrynthzbezo zGD)kFU9sf~Nk+5IFs%Y{u58}PmQzGp!iXHhUQ66kb{Qqz344|ZhI3bBlAz!b*(Z|O zdIgkk!q7y|_*4&PGTXqVLD>b`{I|aV=?+6J(6(&jnYn!u-^||}v^4frwl;op8jEd| z4aJ{MBiJ@|4(3nFGwVm^1w;>McQG5w1!HMsrbrq|H`ZyAUnR3;&T;t4@9JtecCMh8z(Cm$fN~h@q>&a2-w|7;NWr?wW)maktGnaeOd>Hs^a)Hw1i(!ei_gnRUXfZ8n(4$W z0}+492}i`%GUmov-H+VNHKBpjQUzeXUbo;=|0)4z;F+Q6-)h_(M_)Jpl9F}$ge_}il zYBP{flq-tx1{C2-I)>v7-26Bhui|bPFd0mo!ZJ1HMj#%{c;^}w&J^sD*W{PY(b4lL zPS>-9>=JMUFnL3CwJ4`3N{pkkETdom1J1tkN4WyVe?6r{6|^UGaQN%NYQ0z-{O&j3 z|N6mi=5@W!YIygbf458DMLgO5@4o-_Z+`pzcfa}0!}IcHg3CRT%WYxe1>Uun1H90M zLdwG{IM#4fY_735&e z2;{$rEEgOr3OkfjNE5}tGRMr3=}XEQCYv*NBg5cg5$o$W zo&u6yW5$x0Y?I7oQ8WqyeZMZwg+QQQc>qd1{iomjCb*n%2&`SR1X}{JFOSt^;)=O! zqc|p308r-z9=pLj773eRMKH9t3}Ot+`FB>|zpCzILmfm`)g@~y+>zKX8j5EWGZAB5 zWE!~Fkb|uwX=576e%(7|Hsd&RvgP^)xjsD#$fnbbdmap7mb05pKG%5ZiX0!}pf%ZY z<)0vFaIy~0pU>Djg~3fV3JoG^o#Jq!MVTgxO#R$M>|&~M>^TjBq@+4SA`^*ZBYgg> zy3<Y4AM7QI-So@)q5+WQWGJ;>_F>A8=Y1wvFVu01UT9tLC1UZ@8 zWfZ;p@l?$*f#u7Ja(#783*f7j4Q$8DdSGH9%r8O0Rlt@5k8l*jvD*Kw@2aaNg|e>H zb5Cm0&Vq!3^Q4$8e>y~nJ5;Q@bWPzLQG24ZrJ=T+Rlzq|Olg{mY;2RU$Ib~GPEl`j zgcLnx6da>jNFy(z!=zr%;ml<9g@+}V7vEi4@C3;#>oJNptUmJ&2`-+<1A!?J2E*+p zdpTA;dZNHIX=B1Sp$X&FHMduLE);N_iE;pB;G>U#;;j4&Fc^6`zW#%+>;#HKWcc^i zBYUa%2$MH5aa!K zn+B*fl392PJpi*zY{NXs7a(Aaktdm8wCMzmS)2-qP{9mH+!Z?cXu4@KOqa>IN@x{D zP<0IwjoHe$>Q@+kqZ4(G-_gEVhbg4ja#%-LTMVH!U~pzEyk8j)YratQ z&r397US+jR%cGu{Qq~$3XmkL|MOoD2=rzq}y;7-VDokNT6eNc6#ba|B*j|{C{s$B7 z)q+SRd=t<;fqENd_a#%X0RfKL4P5FGTV$Zgb%xhVQdo(-fM31FM0^>gL-~D_k27-^ zsv7`)Knr*v7VtnUpfzYpT>!^22@N`;d}wqgVNsaIOZa9?0m=zRx;(zwaiKB$4(&Lc zXi5U8)(cUW)?rl9EeJ6wXOfw8YP$1(2IFr(*2BR? zvj6wv{r`P=c=7S$!R3F#f8n3u_*((!n;iq=4c1Zcj4;hZCMYdzB7Vq_iv}5v*JVp? zHH|-3KYT>?1%L#23_ejuasnb5U0sJuaV{+u@i?|&r7%1VHapA0l;|W)oNak0I^lN9otaCU%CuLd= zdB(#b=*V9=OS&)O-yC1!@{K};j2O_5`|mT5tRrW!{%&7!Lu z^DbgQ3kvifW4#C;W7}EAzf0b_!o?ROF^H#()7HKW%zFYkEqb zt5sPdvzX3`J{D)2mHaY<3;z$S3FgHfz-k=XO2NQ3>Xj~6O$~QjMvNXw-&>8T^QCwAZDnHX+Ft1bCzwO_OK%B`?@#42HT+NgaUR zkPA-fNpe(lIuC$B1!!I8s1hbB46H$xCd4nuQRA5dewV~ZgPqqfG!rNN>WGb~G5zJI zd_@36MotjC7YiPbFW%~9l3&Mi3X`TB;M$F0AnpQXbgH92ur`i9jm4>Z+U#liU+83-na^uKt_gVI0CbQC< zCckhcsso3;zpk34Z^`2 z1>V~B9&dom$R@WNvrZx&iuWx5AV*GCRFCB7;p9FR`~loWx0rGDQ|;^cbO)Z}b!e+% zO74ro$0vI23Hi2AD$P3u>i)?S)CxVZ@kaPD1oY%4GPPq3y)So~cFw;^FVlp3yT&RXY%Tk8nXE&0Z>S81LUPa?5cMN6;=eI{J z*(Deiha_}>qG<#)S!BDzeQw1<4Tddd0%KT4P@Y^M!@q)UY#rX5 z+XjCjlJMfFa=0s%=ZeA(&cx`yUsox4go$A}TTau*nHvRP23uEQMCmk+Vo_0r_*#im zEpCizoWio(gfH1ilf&)PB+J!cM;IoRKsMWX1n!?>B5FeYco=wH4-4AWExd*`C~Ws- zLpo(6ZJ$vIdv1?$NuZr0Q*dXb(cKF4IXW=5LHv|$00|8~eG2o1dm_2YeAEPoLO&hR(3X%ehDP-~ zp$b|)+am|>i)&Ws)(48jxKm@jj|O+vXz$@L-pK*q6A8UDhjuSaqcf!B!QBx%q`IK& z=Mo|TH0Iqx!g}HTiReF!_+dm<%gC2#$DSE;4g(G$xILs+RL4(O7SWfde|`PpP)PAN znHgLJxJkP4ku1hQcAP0LEc+Av&;k=}Q&_(y`Wed{)X7E^YLLDkZsPPE*h49G?)Y+` zT7jFSDlox#{07}!XcckCal;S9Mx$6Hmx9#Lsaht|61lutuiZZ*TV*%7TQ~Wl4-x)= z?Rt_rU@CFCA@++nMS)p@j|)KJRx~xEnbl6X4WcGkeg#@gOmYX0Et={sf&apIJX~yT zzr!jZhMbYin~bgRAO}WhnZ8CPc9xkYLDUCqBxYz_D0L+hTPQNbC_Nflc#K?1^NC>C z%+x4DsV_M>jfh%Z$=X0XhWFVV1GZUw1vZeJ0PAtcyU57G#NXN1?dd=L_Im^132VgG zto+1`dj%r0i0onE^{^j!>Am#3@1L2n`e5)-$rq%dr_WIIjSR;?`FSujDMu60Rp|yS zm&*Xr#Kuy@rm|9irJWMDlD45!F$&mfIGJuf5N#<%Ik zimUn@KE4a`keg-6>><5)O!8_rb^UVbg@gAE41w-iLW_2G zc79a!IKtQV6Oht2f6v$>1z2#@HPQnCZ>+*n##r6Ln1qqXLOTryW)x7RJW!JpdjpDt z*8ASxA@!!lV2DlygYDmSL1Tz7*^Pi-cPRmL`P# z{|=+~rfWc~v}+Dl=9GV``YPZCCxA7O z5LS$2my%0d`ma24IIHyfA2y`j=y*1ZQo#Fi8NEJzMan;21PGNpWG=}WOQ>t5Top0g zdiUnZG6yyaUPMs*l3;`+PEo~X#PyIwWw}wI(uUj+o232(_!jU)l#8lWJ4P8@y<`%? z1V$&H$0OaFX$)-bL&eE8ujkGOn*;6Jb`%PO$)W#?lCM-88iTh*p@x!Q@BN3V; zZj2u@5`(my7I8dK%}$bLoiL87-~bGap%GqEyC_V{unZ6|k?eC!vEk#vPHLi(7vD6E z7lTww4|fri=j&#TQc+e^l3tz$9i}5k5o>Z**g%r}wkW6&>)7?e1pw8@K|HZybgj|4 zH|Jmf`q#tgFV`F4Ndn7>%Sd)USho|Rl)M0PbLb}h!MgomWLyEgFlVpFD+1x%JImIJ z3m~J25qoU*Ep_GQZ>O-mx2{NnpP*km4imU19HQum#wbJf17HjSU8Bc4Pk#VFT6A2- z+Doda$g~Ku)=Pr4L)rn<%n`PYFua;@k*k9gVr8%t*MCo zY!Ez5veTg>q;}B0zxr$o**LYp!Yknw@2hNL@>CRo~y!XgCcN z`LJVlCA#~Nk@}QvBqwhA!%B8dK^Z9j8}XENV=4-7C<{-Cv=bWSOEN1`WVNwED~zfu z8ri5uPXgPM#+(D3U?RM&Q%8#kUQpQ#J^hU9LC{e_qaH#=iL?pA2Cr|UYp^~jaGFfy zHtGb;fjIeGAkOwez`G0(Ah{I-(Z;LKf&hXj_tg>Y%QmKV_M~Meb|YhKssJf^aB_> zB6@++pk#6ln=}4wA(KMIIa$3Qh-5$}i^;mkn~jBSBQcl5$mE{_X=ZJD&o2cRPi*0l z_%fcmNw_U`LxlMYfaEo8nLVw>m=$9=U2J6Qj;yn4G-Pfuysyaf9xfgU1)JNLV&!QF(k98S;gKCdwv)ZIQoWRIj04oBHYwW2QRIJAlnR;#foXR{2 zHW<{i;X4e>&DI}GjbW`${4avkoId|Ud>GKl{tJj*4OSibL+B}ikOM#DuW@+&rJP+s zHy88=sui+ZRIkLy!pAKkY_Z;TdKJ#dzkhf^_2uP*tsuny;`?n)JTz))YKsbvTYj0sOsKzV2vO)4EGz^I}Y--XuL;qCt z+Z$l-Bi`(RUUQRGuF}bLQKnc~1I9%qHN^WPJ{BfL)~$mMy1UVdNGz}W>JUlHqu-I^ z9Y4A$Mj5I6-_X_g*BZ4OH{1KP+7H`U8&s>nq3X}t@C&*u zhh}Z0ngyRo*^^e16A68on}ob^`NCoKp^1F+hgYZW5t1ON#-!|X+1N!huD?Gu2X3HK7;%YS?4BNWRpp=A)3(B^SnFho z@tKsL{1~}*wW2YdP|Y4~TBnA}#acLgo9!Lfj;>zSf^~LTt&0crCvC2#G}D%Dj#@lp zg`J%|PBb>W`9(NThi!&hNaKt!*TFc1s=3quCHx9!$hAD?c0mNnn><`&$HT(46wJjzQ&B0A|skYk`BXz zl_w)GUis4ux4v%aReS*!5!Xt1X?i1tC6{It^bD*`A(is1i8xwv1VD?I&;T2hY?nQ3 zmBTA%H-c9cAdZhloM@};WS^XWO@dX`)YN`Xs)wD?IIZLDniYo@)DP^VzRBm!gS(-( zdh}#0w`C=fB%vlO##~}buL`StblWJaI&|jM$d8Xq$mG989Y#DqT+PzL_kvcYgjh4 zB3Icn$|}F21uzU5dS13-y;Bt)I-PVBsVmDS+&LQ=d2E1FX-s;}=*kTrI&Guqn8;- zts|9*@dPy)fCqYCRA0e6&vGvn=Z^e+(GDA^3L++ymPNf@vhNbMOvwttMe5*JtWu3~ zd@U-xpuRQ93kR5mup>nn4ci0IeG!(oE!b-PL{hzRcN+V;AO$Dq=KQVE zf+7<&i=5Tc02#QD0~fxkH^m9bmVk?#X~fV=kOd3ls#&Wn;Xy}cMJZ#3G7!O=7$EXQ z2yAAJ*P5+BO4Jm8d#{^*0Tu)yaFcD-`&l(NiK+rsD14chD!i5pZGE8e2^TR~A0h@biK(<<`lJZ2>aDJXxIf+C6<^H0rx~ z9q0u#%HjUdA7jeDS_|DotIf%aDb$bxGB7<$DEE~dBC6n7r=;H;j@PS|W&iRaP{k4W z63aSZ#_Q1^qNnV%i0PhV56ZH>qC~TI3fw(A;B%20b|n3vm$}AdKUDvU-?#_3;c&hfJq&g#}%rI8#q)WOKqOja?H4H zZlB}mxN$tj$u9&W&_un9-d2_iI&+OeU3CtKGnjrBcyK> zN6k$zawEu9f6op4xVCEUL$wY1(kb#vmEVGwnPG{SY<4?H{AkLmyp?%IVTa{pT8rsE zFis`72=*Y*@kWe6bn}o1(eQqEPKsC?YZsV5>(~W(?U@`=ylCBC+k=s4rkzHCBmTM5?9$ z^i@`3*(I$hUpS*W8r{XJ#cX$1AhfF=b|iEV~divP;qe$xU`tu?=5=F z2|+U@7ljYxwglj|A5xg404w5&JF-HFEtRo*bB|LRgo$Ym7Fh@+SC|+v4$`6@85>}C z=&F~MpSI&nW~!DiYP@PotAR^NOYeTHWlhR)JiOR{dMV?^@<4L#coqjT2fByC=u3+h zb%ya$a<#;5O8No#*^^vEp-X&&R4jY|Ft-=BcD5Jeu!P9(?S=+EGRaMN;r(5gvEq}4O|X;C}Zg;y!IUla|2?1 zq{U~%+%lTx9574dsc7`novyjwlma1XiTuQIy`#OlIBc_Bn7?Vkp;ky;=oI1zAIU#m zTv~Uzll;+Vc-rY#X3;LGLh6~4_kT1KY+!@Rz0yN){cmTmTei-8 zEQRn#C}+YWeyobzvVAc2)7u+!X8#@A zWbaZJ?c0scFaxlU6?7bZzR+q#*N*IIR@2!bjU zBAsfnYJt%%V78~CkIv<6VM5!m8XN=(8P!!VDwJTe>I}XKTUE`c*~bkVN_GqLqNV~&Yn+QU zquuCaU141g%+!e%5h471&a<0Hrpbp}&n{1a*P=A}>mvf)29;hk9CHof2c1RgSK(ro z!Nr3Yb?SbX^XK$P>S!9NoN+OMLbb$6_Wy|UdgLGBFNMrWmyjGu;QALk$%?TA0tp8( z=B?+7$?4vn{^PU$t51xzI9^K4n=)cx$pEq0(tPTh=3C&kX3atj(CM=N<>~Ln&wdO4 z`=_V>+-s=8An0wp$Cm62{kO2w?Y}>mEg=E@0SSvkdm5#1h=`)4-IQ9e5!+)5tjw-( zJ}o+fX}cyIg@=#H2QRmi!(m+t^?*{IyBy3aPd~vUQB2lw55j6G<5|+gZd1{RFWa}N zyb}@EVstfBX@=vEAE^vz>dkLT!4DEq8IO&T{dsk%$LfUOM91ozqqgLufk6>?7db8nq--(J$zEAp zvoR~cXR|?l@!h2@7LAqKeavGX7&O&siVQDo^l}j6a>gT*4sDTrG`1AVMeZz9Mp|?_ zwH4_PHwwz)bX#b8$m^{Tiv5|92`)gBht}#%9%=%Y-H3I*+t$@KtMC2ej)r*&jfk8k zX}VsnkzYcoM;#r^zgZKbI}k%3er1FpVJS?cnPvGhenVuPI&FYCnl7@7^YJ;x8Z)Eu z3Uk^e3+B;BT0FYEG~@^JMHtCYV#ku3!kH>iXIzk! znrR8zR|cZc!zTLIh#eekt-63hA~g?NgzY-CeIdP*p6BUynNv24VA<^_e9 z^q7m8yR8qdN)?n9`L?xcmB`^~DP+{Z^mNt*7Lg>%toX^cxb+@E>IT))1M~w^a2fIk zY^f+%qe{}?rd|Q9lRCU8X`;VpRY?WSMImV5A&_>#mEalHjn&OZ#7BoNON_IG+sPTo zF{vR97D}*9!vLoQkMNw-^H@Yo1qUC4TC3+w=%7-HX+ql1k9PQ@P494Gw z#Ahp%-$!W9mfn3PT>bzp%NV{V$={fsi1L z6k~BdfLhT}Lbl@J>PlY1Df!JBbXmJk3hxMgZt4ekNvMw~J9DHF07U?e%Cd|n#i${% zd3YCdkVKCt0jC8SfBq7%S@Bse419ge%nOp%s(MA_MgoJsk!^oCz|UDVEo%h_W30=C zouY+t$VO5uk;Q}}n#a+At4NavCm~pL$1mLvyU2k7#7NEOQ6wsD4Y#$Qrbsq8a+rXL zutt&mG-iQ}(8M*PE+(@+x<5-hB|5c1q2JAliz_^)@^iQ^$6d`xyd%47&8))In246^;1!|nPYIhckloB$F4&e0%M#`nFTMt^BUfpp}6;PfK@c| zCT(8?C!3mHKzW;mS2w_Zk;Axk%el9^41>cWOPKOURDm7S?(rbWzqTD%xQ2<83 z0meEFi6P!O>dD*~bHbFWSOyz4PL@itCBXvF8`EN9tPng2rbL*G@PBY59>GB4$Kopf z%efBwqp#S>qpn!{BkxC_y!O8W^(88z=3NDf_P7yw3~9S<)0)Hthi|q~LV^pDP-_!9 z-?0hmJbo{#PGZ;yB|DZbG}ei~lPhu`cS3K!J^tacScf$5^{KyK&sQEHCVtq(p-&4W zFaL0Zu__d7<;$kGty00><5qfGTeP;NFuHmN8B^I0Mt@kwc*MJe9n$ohgYZ@_pI*ij>h?K?Vn;llpdusoNR;USO<`bLb3Si-5F4d2~oZ z@I@>^O|P{ttUgFB4FgLp!mR6Z*0>kC zn>{U_lS9kB7(~dDQSm)o;yr7JSEm86>oR;2IPObg!s)IHtD zVy<6pW8oMOysx*xKb^mR+{#n<=L`C4_FK| z$$stCAediwIS}NRtw)!$rb7whKOFsL{4B)q^jy>$i>3Xtp7x}y4_jRgyzdhCU*_ zPBgamHy+9>!oC`V-`_9F{VJnYvFpdxM;7h&#L?#ctQIEwwr#1iinC*jwz>;b-_kid zQa;cOrPH=OJy+S~fmHRk%T+@qz^N0GA;^cy@Pe+_2RbJ^;S;fH1+|R~<&l9^qoZfqC}oPi6Ax=t&0gepyfGKf7f{ z6rM|tw&A{Xq{*g8mYq$v4C^gf6zI*^&a%Uq-H0C65#_a=N1JlZ&eB4idX=W+)Krwx zc1gKbZ#apH0gPU1Zb~DGF`EFSzSkfl*06cFxOjumDf~wMamnIs{0$$Nt(p*WCpe0$ z4A&ZF^3>an?IR$clrI_uc{7lcjWYfo;mg9D`d>2(1~4DjB0hmPkpO>#D$_Ee-z}e*epeq3%1%IR578bOtdKnF z?xTll1Lc7hrFE-yQFFo{6>^NP$+VrEwyWk`LeUL+HdDhju$+`JXl5mW5RvgU;Oyu&5QTP=PxFI z`f&Q{c@Jz%`(+-9-^-!VMXXX@*8LG)IUh=#SJ$pbrj_SC{v~WVf@*i($I{DByy89-`bqoj z^;vNG)mFMOEaeA@ZX9gdcRdo+0pxj@4}CD*JE7-5>wV-bnhneLRSstkqCDw0Nrf!m zV7|kL3@-_Gq(;8sj-kSGDc`GmgQx&6Qjq5owr4D`hjG7SpecpSS*l_I7d5@-aB5HA zw)n|U)za8I)a5Kc9R;u=u%aN_t_!ZwG|@9NYt2#qa%*$k(El+_Z6K!OO_{UfKW5JF zs`3gh?)p!O6=5RhkBm&69>srwZWNv5YzeN$Zt9NF!U8l9VTO2EyX9)IW5CfF2X{h( z3t@ey;Nnw4n<;*W5CnynmR50ZA|qVRBfQVzp{M-n<9#CpL&NhXezyu9PCh3S^~}_4Nz-*G z(F49^FbT+$#i@3Q!7YlHhKri*fHucJ=SK4+FD4xMWsOb$Zr8*!+6SEIsIx4#v?*{= ziO{nw!m%5WFynB@B#*nZadSVkwoQ81AM0-*Tk9Z0sk&DP)GnF)3k$_f7zL zB?xir#V5E&`{>Gi7zJ!M>55B@Z$$=NwgpUh;ZSr&hlSzeV-E0g>-c1KA2|@mY20o8 zu`niq_mf}=i73l-PUaaUwGYlNg=4dX|6>-7`K8xH9`qKxz!1?8z4c$0)Opz2g}bzF zO)N9^mqLZjG|Fj$LCWQ9ol>>Zq?+ap=HrTr^>T{!9_GST$N*x*6?0;yh_fr2d7V)v zU&B?9fpiS(ig&`#wswt!4L@guMC~i_N^|g5Ef_;@+Ug`cokGSmpsRT~STHR3k>$_wEcw z?V1jaEx1D520dsE&oM*hM@EDMg_%@7|6+13s@D+vk{~(UmrV|1pm+9g9Mp7yH@BFU z4(I7~c^m4DLfAyAA}`8c{(#Sk!c{{`c7b_}^c=!4g&6U0{y~i>;Q#Q5*vwJ1W2>@v zM$USM`w1H1E>If{=o`B*j1B`S2*c*Od3>T0!^_n~uYD7)>&lLw&hsumR@Ai|n%^vd zY^rq+O$;m9PFZ_6n3KPiI5+Y|p*b;WvfA1v553Ybsn42IsfeEpgHG8!0%$Bub#6E^I{ zTJ*U<79t7&;e3zM#Vp{~c!*@(nECOeV_Ss#BOZsHI{hD;3Ra!_jcxRhV&3$x>rvI5;!$?*n?vX1mkuoqapUV&#{BxZaa z;emS6a~TDvq8NZKEnSPATlaSuoyf&&P4;7|1Rr_R$IAckDS3Fn#XZigmIMjR{k$!gn>LB>=E6RI-4 z`=^3Hc(M4BGy8|C`o7Mk@}-wX4@JLKD@xi8f;_UG+-jjTd|GpAGuGww=l0@b2r-~5 zAfraVDmtN;JEno3-RK=KVp;W>s_ddKLC)bt5h>3RYe2feda77=tz1b(7Vinx*rt4_4SPJmiLW7E&@@U?sRI37JDE}AoDyVvaIkTglKP$&=j8)Pq5eIrc z8bF6Z76mobpOaM{W=&irMUWlY+1XLw-(}e+t+bZ(8IHRFsg7>4xXPppF&VQ)>}Ds^ zAS-y3lJq92MPX;Md;K|CB$)b`s(Moa=)8%NMO_+(=Oh~AtXQpM!;5MZ+(>%k^vUMf zXcz)3sD=X)2$)TdE`LmE;7<#j<1aW4t_zUI^#YdlE!S%_(iQ7poUFxy@5j`#O<6AL zae56{8Zy(3to!ojx>O~aTnnCve;F9M1?XlJ+HG726dI@)eDMjD8B(3)#-A{Pw?yd^ ztAv1pifl2c{2`T-Kg0fq5~K=P*cUb-v~EvuV1BTr5a+G8TVFt9YhU>vAOe|`UHB?a z`C$~HoEZ{)dt*C`fXUl27&9p{uIz;7BzYlm)Z0$E?b@p?9IsiY^%^c3IJcbo7#$_Z z;?e#vIEhC=y=$VJS=t8g1z)8l!fK2m{j_&O{^`J_YV4c!svjDLl7ISmHZw z)mnH@z4)H?0gEd&IJs!R-ExQ28Su8{P!yn7_dV$y%%sb^)+7AAH^9BAZ# z%5p+yVK*0c4YV7Na5MGJ(j^M?dY)gcD^c77aKnKnz%Vu;l+ZX=IH@?G<*>YnQtPq|beCXuohVnm(a)zHu}*P!=XANztknOe(pa0$^3gT4ZRU`zY} z1d-VaTRL_q8(K`{CL|r2K%A>4Y|PRxF#bLXJiM4w2?P&SJ*s6PDm;~ycgJDr*{^HQ z*v)TlOV>K@l+a1m83|Cp-TSxRgEwf)Ch;2g-6$7WfG;r1a~EZ?k1T35I5{}k+Z(ET zBKk{N#DvsXT^rXh0LQL%PQ0$FuuKxhG+UH6$!W1U+bX0qb8i z@G!gALOx;j{Pu9%F^D{hv3LaC*K|@2HF~1_77gI|T1m&-XbqDSp0dkrn!L?`QEQ}8 zETmo}9@JJZc8i-|nGtRA%K=V?jv1h=EO5bGB z!{mhq%xNPf!?odWv@CS-NJ<#s%DrTKGT%05sqhlR;rWuCg$%iY8<*xOOu<%iLOdWM zktekQ1Ob%EEf>BTZN3)Imj*onSU%IL54;&BtjzDB?lP{f6T$H4%WJyfeyveVQd$9X zckQ*>rTyOuCzV&21m!|8`4!@pkwmT%OnHdRH?K&^e3Q_^U@lM1fG-wmD@j(92x3n9 z9m9jY*xyGlBU3L5O~p_BUB1GKP`tnyQ`tL6&^aXN5~aqdcJr=tH3z9~R6uMZf@_ZK zCB>TP&`a|^tAJ3$igHmfv1WYwc;jt&4s=n{mf*=G*f97+sFJ0ISqVeY0BawuD+&g6 zU`?|$!KzrmC!wlIp~(R&od8*ooU&Ic-m^oYt*S}u)ouQgSm--d$vPsP{=Ngji-d zs^EkjNZ#~aA;XZCB(pncFZ*KGq8UbtPe+v;VW!Vk33BK-g|_=k>cgx2CR5*E5-2eS zcT%Qp)*rN_tuL;ZYkc#7ynq1T%RR?7Tm)XEd_=ZjmG8 zeZ>oQ6ukoy`A!1UFORt&ZQW{0r@Jt(7~X%Ld{Ygq-sN=Dz=Xrn1lj4~dJ&Z93$Gw! zVb#RPa6GDp1v0z0?)BV;@X}&v+;|T7F8ptot-k(wV=jZrq)6IBrA#eCt5~Wh+XutZ zFY<+sJ=d(1b+xxORoqgrwdQ#q>Z?MqeWwTdnO#Q8MOl??4k%9f>UgT!$5!CWIR>Kq znK(9J-Gt<6N1q_KAZfZb??h+Gj0$|`rj})yB}GjP1a2A`qgK)+jytB)Ntnj)IbUL~ zN@f?>vvpzZxCr9~h9DYvx}4$ITQ1DUq54F3DTx4=!KQ+!#3O^nv}6tG>k=>qxf)I3 zm>PL5+GdB+9o4FM;no3rOS^&-z0Q(ZR=M}7c$E;3TXdUyuKSv7l;z#;A#mjw)8aNT zt80HgV$jqdtmC@K8yR%hLfm$vzW^ab2AazzAs%JvBB>}YMi2<3#1?l#?Z#;fM3z60 zr(}LKkvTw;e1Yux4Hbg~xS|%Vr)Sonc7^BDKJ^MK4!t@?0{eyQrdid82M1R$t2O2a zSRRmCmqx%}5AwQRXZ69;-~H3?f>6xW#-zz&#r>rY(x_|Ss%D~@G8uPa+IF*|VNQG; zsv&@oa_41<9Q$WUwY20!VFO??Ubd7G2+MM!-uC#% z_hLBbZN#0%al2fMOqG~jO-3pOg<_@~rPTb7FhEK|_Yp`}^Cf+pw%_O<1O6HI%3+_n zx!=cb^rE0-Sfn8{_O8l=6VEi8qmKG>i1pD>WXT~_I~^8W5I^9dW~eAYKJDVi=F@q$ zTw%rO%P5=A(U1Fc1{|8MKNOAEOx8pFsNMRBZW3XN6*L0VQzEkiH!RuL;rx-yl1OtG z-q>`cbmq2U%oi*i4ewqb*I9uPBhlH=!DF#exVeUFk=j33ohQ*~9y{mg=uWwD5FH8#1=+GJxZ3CcJR6wer*w8xIZ`BfCp37S(dPse zk|%q6X#3Ku&N*MDtKXx%Ydq(J$AJ-xZNPA>Lr%+ZP%Ax(FKvBBhp9;g%+QN;QbaxD zw#XXYaF$KiSN*ga)HDu|Is+WOv&{cavSL4|<{LeLi`oWW1|pqnK-A>v1F3Fi4~C*O zj_kFvD5uGSp>PKZ3X-!WaDs3_Z3QC>2?gNvzFGv{m=SijO~C%??0}K&U7#vFNi#C|I8O1HUVo3UcplEL3r{`SyL(ei`W%%-?1ppFY~`^E%~ zsIqBr04=_7)g|^L? z&XYYywsTLL0~?2cJ^a|!LGiQ0-7L_NoR->~R=7w=`cV@6d?7XIBquz}2a*(8fCrKl z_0A(m3w|ze9sy!tOrS48(8J$O6!6pc49ibXLV}-%5tDXw^o}1d7i_8JQEcXVL>Bh} zD9aOjSuPgk4PK>-@+zmp{OY&A{q_FuzWYwNzuaUx>DLU{R>UvKkM`X)@=Fgmle3_bNcDC0@8LlqQ^@(mXR zJCqSmAp*7NlcR!mw<}+tNI_qgVn0WbdS*z_R3b`4wi;{rCjtb`5ezQXGv97 zGJhFmsAAXy5<#%{lhB52M=+d zMCP~9jVbz_kOr4gyrq#`RpSnxwD1lX6QK4Bb?CT19BGFyX|B#fv<@0=Ks#a>54iei zqvS4aW2=od@%Ccg)q491Qo{$VB7QnH75mcIA$+-Tl*5j##?M#Z9sG3ckW`n-sgI1t z2GQd{_F5}mfDl(3LGKXj4}O?_tW%vsI`{FJ=wTIM+sqIw}sA z)_qW+dsCgwIQ`2gS|rQqEQxLpqg&JOF5cf4Fs<4EvYYoG&dw(LhMA_15S^TJ=Dnm3+)?oCnZ6$O%faShAo@i&pO~=)7qYAl z{mJeA$eccg4wN&Khxa$eB|&~_rCmaZ6oazSAGD74InHe|j%e9xw8zX!5x9qwPEaDv zX0n)_cu}O(z*pA=YP7Hfsp)%ojLwUDXCGd^JpC)K->fPDYXJ<$9bMRc z*Li&%U1Mb|k`>t9h*!o&?1`1wl>Qovk->4mn9fN&)W1aDFJtkGd(CL*lFsZgo%!yv z-)EL=&Kb+P>uWwoE8B-$4H)0tFFkt)z|)L~(^5 zx~EM7wicX)oh1DPPs7B)>3Dzfxy;iRQA&Hw1%yoZGHq=+#_`s~yoOlrNr_1n?U6|v z+4j`+?Xpm}>K9q%SdUdZ-udIl0Q0I2d&xP&4jx5?+rhlLC*^uEBSJmT(XTB+XpA4I z%R|MCOi-jT2YDOYX}XWK1ih&;M#P(BgWyruLt4q4jQLdAl%8A8qdO=E?F7KB%35Z4 zlkRpSotJ|AhqX#mr-KTKu)Eqf-g`zC6~ms=E~;O|9T|a4dr7AtF4|vNiRpuxV9k$* z!B-DWKp{VF-sh`+wjH@t*JMKKE9r{KUO#NI!3jUy{Bqqcrpk|<;>cd6Hj)2-)=FG? z7KAUFc9809Pj1BkgVfiSvgAkUP1;Xg!eyD-7S+*zZ9Q{17AYIe`(%fI1X@rZ!i{ii zrr5cvl+mH1@fMBf=G?|Q$1tZ9%z;H+udpEB00(Nnq?Jcv{C*Q(*J7iJ?bng__UZ^< za~%G>{kV9?BYy~Gbg6%DYkJ=6^goK!%DN1XDQxx-)n%v~pPU3>cJCR%?MLRky_*L$ z(4Jyb)RySb0){Y?8FX!SUvEDeHHh^}qIS4`dops@}SRA%<=P>`|2V@0OQgS%x`hCTwTei@_FnhDz#-G^Z& zzBk?t&I=1D#mlTo;7(5{EXTOT@|hXww8fVc`}fsd_^gX`BB`AK$$umb?Z@=Mr=0#w zte~YA*;*M*+BDkS(YIJ}rKKonC-EJ!yL2|B10-CRZX@uoVRd4qp)~MJ*O(cU{{lcS z6U+wV+hu5BsvI-?PX$#L-)uEHQVKMoa3^F$;je5v5vSi)*O#+=o@Z_d8-8{p)-JGe zcmD)uwGZ>hW0h2#Yjlw>^M*CIl=$S8e8y?7O>n?tOh%EOftB4ru98hmuty9sq9nHX zzL#ea#T8yeP8i3l>obmWmb#7+zZH8B`FLJ~MDjVSrT}gkV)lH1d$M`lM}wl5f%`+- z09iv^k&l#X=8u;#7id;YCE4PUsIsbF@h=(0AL?-*-Kk+Y zHE&7^#FV6^3zow2qr1{|APS=z)aDNfa7YW8cAp-5c z`h9erEmowBW+bHWTjH-AP|WfXIY9^`O5dGOMy5D^VXap}NW=2zDZTDc5D zdc?^$DHT(Ke%=BW8W+*X*1k5SCXMX`GnhQQqkvQ==DIK1((X(Ja}xn>59YS9QkjGnP;7I(smYUxPm4(Sv7F{t>%8%`?K4Y)+-4XBj=4K3otFXG z;5(r7_bRpOOfcN7aw?#%uaq=dK6wBH$7e${ZSpftD>EkLLqKb&cGEetuki9rq7%Xz z!54ZJZ?NWd!I{_fb}-5U8*8YR81xTy%BjoIFerW7RilpvhZQ{5ugVNG9Wmp2mR)L)+P*?Bq9O3=>JuS9$u04MvnHLXQ`7 z(@;|W9r#If=aO3-iFlgqur=y)k}q)8+zFj!naW8sD^o(9eT=vRgtQJohn? z=e|;CjIW#JVppg<_7&H&FW0a;f#FZ(4Z30psY|l_V!H--!$Eh(<}D5XXzFCOXH2OP-s!;sja zmQ834rT9U+rt~!21U6=H0sRMpffQl3S>^-;0&W^F z>tI6!I`Mg{Yk~xZjYneOW%+5EUFR5n(; zo!27uN+gA9Md2SY4kPR+>1EFBqE7ARM@3)~tB?U#F+eR(i%}ClHi1bbd1-$k%-6^d zD|RNHN~Z{*bFCbB`UCpSmQUN5`aScRJ)X}?Kyv(1C2Dil`0u;h>1cZyr z^`gm<7gs7lkruKfg(IMk&DFJR>v?5RMlOo7B~1tmg*<-5QukhI--;up>&D#(`KhU= z!g*MV1R~XBlyG>WvlTE1-(o?jy(`W&mN#00s_T5pK6bRa&}`Ah zpOP4bgm4E0@Fh&Eoc_E646{TLogs9!27D4Z6Qd-gi0G596QLYDefsR_uZc64y`l^g z@*5nKa}YjqG8nUbg7Ot=7iL`i*C+`I;x`=DE4b{<1h?`N_NjDHQ%geidWFKXk6^II z4*#+X*GD1oies*YAPW20`$!r0$?YNz|ju*DgnaYl8TZ~%$XuKrbmK(h5 z`_a>bnNQjKHE>AF>_t_9fEgb%*%blfqG$~LqzYZh+>JNDEzQOpQG$tvug1~4MTUBz zD#O!g@^H+q!QmOF3Gfy%g+B>%wDx#5a&r+&bm6l5c-R4}-zHE1Ao?;DJlEA@wA zx9XpgX}~9ib=EDhq{hbe^lXsG0ew+og6=vJDiMKuD2IJR;}gm|*oG0U@pi2rbWo7f zHd3hde0xh6+_rZWmYPwG5-Cj^LpbA&<1F;ByquO#kJYKSHzwlh_#Q{=rSs>goDQn3 z1tCDnw}bLuvS5bHww94pN~kiIdzIl;PZe)Av0x&p@D3T(u@hH){0}PD&f5UMLPkmDLey2YF4Je|1~bdQP)}3xNXMbwlf^=?RyfsgSL_BEhc_*q-5ik zLX!hKdu48RNY%2}jp7b-B*C{)6vw@sl|j_ce33LwR)|bK?BpNr#DO7h6M(ZWau6!` zRrT{;RJu&|d)u)E>rpuWe6ls)zX0kXq7{cLQfN7~o2bx3jup@k4Xv9&|w2*rp1^}rab@hi@4*mK~k^!k6q;Ny2EKgC9z zZ+(-`8=32ieDDaWDY3PV>cKpJC(k4KD|0|fIG|toM|1Q#Q98edsjWo{Gk*%ffP*Tl z*9)gMCFxFFbE`14`k7iq1sWcJ5O^1%!rj!9siw)flo{Y!WsV1sX^fBnks~;$X;qJA+XfmZ&->? z=7Y5!h`fIsoL@Vx=ED71jDyT5Je_p4Le4g;%9ZOYhmQnWBeW*$jw)LqbK45|(%pI! z_OEcL0Y6{H!y6DC@ER4Z$x$8i&=?Sg(I29x!fl;@^6fg5h6v%H&67xQm92o*a#V+& zngRCWaQ|t@odGy6IVfTQw++c34BUQ{=?h0-b_o3i;_C4OgaYfH$$t6IZ5?x~+V4NC zz$T=Xy0yFS?%95+%t?1ou^RgQ-LLQOeseDZ?(e?8$2|Oz*l0L=(;NuZW*qghtVK~w z>N@3OguP1R2;~LH8!`1+e~_s1-eDwk$2DQj>|+co_A%a(o^n7s@`4jb0ox9P829W; z`V}%RX-$t(*wK!Ok-4iw$5P!tOqPic5FV8hAIN0rLS57kje?aQq)mp_ zvVckV&`fESxd<7>Ap~3vn94~KPmO{j&~5OqfH#ws&sC(VZCQ%zFIbA}ud@`_EhS_H zaX01~h}?IGJ&Nj#2H2DJkflT)O4zBYwR*cv91O%%^+kvG zYgn*l@(GJQ`o$p(ZrIeg!hyL3USm>zIzls-QE$VDU42l&PhPYZK=z`Fd!Jr6cz|B- zrnk2~DKZAq0i>fo1b5)qhu8wcELCBH2$!k|dms%0E=XaY53fk4;Fc)MOf7RLv)MJl zN?TWp%tRVpg&frk%!R2WsN{&SZ{`aY@LW|5TFzJdi|li@pb}o%P=uMt>bxS?X7UbV zrwGZNaOp3+hnDE7pNNDw>w@%vV=}P%^|<#}NmXb2NiA#72?IV3q{x7f*VG49YW<8F zW`y2w)OKcffmbaI?NgRv`5B?7%5JghX5M(d*NBrdbERmRvzbiiLq-V>Wf2-GUN99l zBi%%3QQUIqg5O|jJVe{dQ%>UP78~Xk7vEhDk-dxcctQye+YgRdJXb}jE=ZMD(hP8A zcMYmI=J{aC9K00!F{ZNCl&_jqhAd{fX`gkf=kBqqLScIRax64{&i%`Ao9(ByB@$PN zN9r|YR*$R&GsA=*cqMI%-5-WJZzoaO54Md?bwLqCstrh(QdR4ME1UPwuIPJcZR=0O z+bzll&z4=7Z2~3fd`*iB0+CR_5HWnMAPA-1_r<#c)dJ-uZ%GKZd$BRu1z{gT3yE(f z2HV>xyie4^yV{n}q|FxtS#dg%^>(PIrr_Aq_Cf$!R5Xh)&Ts^rOZ{YXplJb0rhvIV zq!y}QpyT!EYp{?=lZKYMK&x#OB6n(zLi=KZ_>z6GhGQ02{R?LkjAZ_O4ggniNP<70 z1b$Q;3NNS6$Jo3iitg-2@3RF5 zQbA9AV`68b)PUI!PN(BtrduVzr-kpYIp{`OV(M}l(iN`#__yT4e zB@J+Cq494tKs|P!&l%o8iL6P|m={CAcF5KACGsXe!LYHMrfeVT7pp-6# zv>}vsk{KCg$U~>Zt5R?L11c{Vt1(iO7>R8R)Lh_l3-(p8XCHu;w+x9d(898Nn*xPk z8ZeAhFh|gz%}RJOqI0QXW!qq*Gb(V_Y(` zPE5aYgBQ6+XoSge*d7IGTQj)|yP zWfR>^i~u=w%@729&t?fb`)NhZ9vkB-y}DH4!f#t7s>sx~84+MVHyC#Wynkq zVIQUkxEo0^Xd9__PWebESw}9JQI@kwRj)*}g|a7>TCs5p(0h|ciq|jHyN>37%|@S zfF-n8FDENZkRhqF$^K*de#|j8hK?wY3mx>&$zsg`!SR%{sRIqYrM}FB*02om&&lT` zrb0-qptVvTQ~V$LXI;$+f$^PTYl;jaj$K*4Iu1PZ4v3E0uLsMO$b_J>Xh|TgX_tbL zp|}+&KAxQ)pP!ygUY)-Aw-@h?{1t{rSlBhlriJNpW<>>9MYZUfIpGuo$JN!PW4_0a zWTE13@*;KX>+2VX(QA$sf#EIkzY85K-aA60ixLFp(ZRgwa1tpZ>ZZyYKb3&^ zP>BeX*(i6!t;oGYnsd%4@Rz)pl{fVmQz*EqZ^+1`b2`iBBq(bRxcB8)~)!$^WgAoUYkvX`OnCudE|1(BE zoHjCQjfJ>CNuph0yG4IeRG)IX|C@{=kH*n+xG@oA!NwMKNvd7ak7IKf z!b4W^*WZ8d9}GrXz&pNiv&l_`lc~_tFqJ1E{swR)W^DsiysVZ)3OV1$W#|}?TFcxR z=>f~>q}yHr?8kFBA?~IXuydNmtWWlOGxf7#3Zv-Xfw;puj|;2j1~2S6o2L`>LP{#c zf1qD)&!PeB8HTf9a;mCaUtMDk70l8TJ$(isrQqzEL1r_N1Rgq;=q{g#*hX`8)x-07|D=RB2^Oxr) z)^(KQMMt96Px=8gaAfsF@!yPt-IkjD5l#lIP62`d2S!`5nS#EEkcH7p^ld4IP%glk z8`O^gF&ZXf`E3WVn!oysXQ03E^?O#^o-SFILha6%R>P(t^+2O z4d=no&2L9G_}iruzQ*S=7%LD`EDg|>qxH*G)^Ml-y`zV+ z?iBuluYEm!ItoIJNI&}K8zv8z_scTbgQjh72UJoWI{RDdq?@UWpqlbl)j@x4RvOWv zG?`zyfkWC{g{&FO2FiS-ixS!Zs?>^CRgdvs3{?aW=L-Li$$fqJ0o$Plv=XhCe)?S3 zNo~tF)J}N_VaF7Zj_e@7_TjhjC)aB%TAu6mxa zH7g%J-Bv$*nvwh)r2u@|B!M2a`nazV1T^{^2?B3uY72>Z$6Y?4GAB74`GI-#DhXW3 zG2iYV|Hp$5Q}FTTdmr^~m~mBshZt9%D67~yT*-q5S0b#(4=Xq_wQzl%5rsYC^QM!w ziS)*N1P_?BqI%hR(vDxhJvlvpb^P6<&KYOgfJq~BnK|Bsm#dXf7jZWGsLBN7fDuPK zh0zn>-`cVHp>ws#0^_-|ce}znE_{j0CiAM(>ZNZ_gJY>C1fJ@y zyN@n|{WOhzx>lsxr9p-7vl7l^)ReOn?#t5c@?%w@?G=GQ;3UnsK-TyQP8)Xm;y8ZC zw+Zv?mIQM&$r8vXN|{I@F#vSLK?@qMAk2b1pzNTlptne$z-D!xLrRdqt?F|akDvlS z!?<9k2Q^RxSxw|%I#!j=j=d<0@d^%JNi@oqOR9*sdO`52OcvKtAm9KsafxS=<}sC_ z=lUn;e-Aur`q4Ko_;a%}n>q>D&}^I*eBfC$zT2vHPp6R72H2}m6P*AbAw z_v*SYzbCOe;hvxFzZojcY53#*!B3yC($BH1r-Lg1SuZYP^)CpMX9q%-)CH`5d#T1- znmD8c84~{KvS*Yl_@Y+G&uYI&%3?JqU{u9#kWLA&?03I1*Zb;)MK!o8imPdcxBTke z)$G~U*UMs&r_XRj?a-zFuRAYKC*K{PjECRa@@YD_ny)b3#b|Z)pNe;rXVrCaI|Qu8 z^eX=!`FQUS|NMt<|53GQyR-794Jz@WT0}6@@jCfu-A$)AvteFtXj{=-LQZ^RLj_7- zgivKJyReN}5&;aGn5&4xs5K8aMs&MP>2!_aTe<^Y>%MnUv)t_HS$EWNv>Yc2+ zU1vPpP-4xjK2LUMfJnmhIxM(MD%8|w`E<=$^PrpN*u+ACiq^7|Juj|jj*f@OrW4vj zIM<#kABl(I_J6j40;hD11BXe4sPG2~&0c)Bf$_BQthz}(pB9dhqeuP|J{&pLp89^E zNjOI&mx7&^-*Q!n16aS);YxC{=Q{9()(h0~#bj?E&NZr*9jxUMC9&~gX=CEo$FB?y zy5?XsF#oo4Heinv!V%p%Xuh>C0%P=bnttg)0SgcvDAxKJJ4!NB5GpQ3{yr?^O}{ue4PZFnisrq7Xd6-)c%i z7NYwx75VqHjaSf=-KjD>xJXGUFX7w8;bz_@ey{k%)!MDv2@hNAyVG9rYc{fUwHQN8 zA|qiyas&$f1=M-)9Q_7>D=Db7FS2^@VItQlW<&Y?0LE%J9$?N=C8}2#q^ZPovM_O&NTCPY#5Pzbes}Qh zaDRAocCi2U=x}(hHnpR36WTq$Nhzoo%pN)|#`~Su|MPv_H~8V|W}qeA)X$*)^#7#p z`{482L@36e++oVA#WcASD6F4dokMWye+{)+KYR=|xXZ=ECTg-l)O|cnU+OW@uZ?=Hwek7!0!79ev-DEo<{@j+7L%G%_d}eYw#KM_}6q?dwjFJY(sVUjsnN|sZYYm6j-r;(Xm%Ms;nt~hAlh9- z#^=+$hZy58Kdz1DjmG7)VX(>m1#%7fcRe(a(Dh9C+}c_roANr< z*=&z*uGl+m`-ZCe@U0`^sBT?~vpmQgje)UZ>JY*kw_7A~aF~|=zzprx(y_w|n0=Rr zx1s~3Kom2&FRuvnIhmGfYrexc@ntsW%(qC-iBWV5AFE~j5j+j>PCZGNbXawug8ETa zL=-bki2Z+syRQw9eQ9_Bo2w;mK=ipR< z)z@?&K9Yh{k~q)@x7K?1I^io4{AizR+IIVzu1XDnZO7Sn($rp5aNwr**W=ySfyDv> z&Rw?vJE^@+U4~FU;>q4N#Y3`OEj4JaWDp)pR64lf;d0QlCGTazo8B;6yS# z;j@M#NLbglPuwtqy$-C&0<00*2qYVM@!Zv-!x6h~`APhVJO4SZwad0i+Xfmj5adzq z;N98z$?M_4$?MnqZw~9Bjy#KIMmsdRnLPNr)vMt4auJtb;O0j4pFmF=ct+3&DhTUl zUewD3742rE!mvqlr#yc1_8o3~qGzI-lXvIv9H1R78`N6}pN0)j zR7|!L*5xI5@1|Ji1tN=zpxxWEr%yJHUU#LgD3$kn}mS_QF+nmMJzF?9mxsGfrsYOBwWLh z5Kbcqn^GHS1F2`3K5Qy=rC>V@1>tltQ0ie@79xM)UL&N=*U~qc+7OcbTbvmKuf!4Z z2y<~PrbdavOXF!lDJ?jj0Rf5dE|^k!lo|42bIT@WF^gK8hp6lzMM8M&EL|pX)FTuz zOw@`(Ci+B=KX3cp^(OS}K+Wbv8V^4~^FHhHae(6m0lnQCq=gQ6rRKx}#$)5!z@VO5 zqOUQnNVv-Ha}J|mjBErjU5)tLEE%hxZjyYelb|Hg_v|Nup%$n-1_?jRN8T6^y~~lU z==|jHgb<}{RwqXV26I%Okl(suM~1DFe52XoXX3RqtvwtmM)(lALJF&8*k0kTQfai| zD<_>2czbvN5&mU`n!ghCpfb~4?dfQP*C&VXUL6hJ?w`XZJ2?K~&B^J}!T#BibI5BU zaQ5yyMWdB~m-#|8*6>#7AqEL`OK-*~lNQe|f><`Bpcz!730RElk)IPMMQfTq8Wa48 zIQ3FYO;M2)D_3&@qw|Om0=e3Wu2v=85%ly|>LuaNp8Ql3_|R-)hLvgBs;)*op)ywk zoizcKukhY3ELq)kn{`@(?@Fa7;6ru?3G6C!6}*3TaD43EvLOy&q4uqw?g}KoQ?f-# z)4e#U(maPajj}AtYA?>O=4u7UI5Cs@5aIIY3y;8K_$cN>?>seN$l~Oy2QbzFwI4nZ zgQLr|Zug=Fo)KMcfDtjh7u$nrahsI^_#U@1ebXryN^^biX-Lktc7iAI8vtZoC>>bw z*HrC6ihXzWUhs+A@|n^gQkISN`41=Y^0we)3!h{kE|}isXIyBXs?Q-)pZ)|(0-py< zG-F-Chop|4crD)6&OLl!9}}fJu>gFwqIei1(quWx)p98F>0LzHO%aU=KXsK?*GxS^ zbw>R$Xr;PPf@qFuMnt1({Ke68@ovlT+F@aCWzw7{M78~CppKW8hg&I|Q8TypuH0`Q zJNVp}rQy@S)>g0iWE;03a}#;N;9kcrEah%&4xbhp$nbXe+(1G2<{L>9JjlFiK8GV& zMGUKT{9=)={lEH*vE zQf9X5Fei2r%ao4dwobb(Z#z1iQIdv^x{r=WG*4)mMS^|Zn?5xybPvo!U?{sBFfSk# z!!#es>}t+sA0vRa-Q`^;RkB=`J&kZ3YcI~2)GLPS)xR1wU4yAZ{Z%N-MsP9(fO4qy zwVq#Gx?R?n*<*Fuyux7>y-p3c>;4y`U&+wdGynw44C$p_pZ)m&i13va zuY$UlkA5#q8MZ!)wn7JPL<`xEXE^z9UrUTwg=Jigh!^F z;k;Zs4auM7>k0OYD0HTaF&oS__I~WIFESE|j&C(B)#?lT?MO!tb zA7VcsaJ?!(!4*Vsr^oa1IG;>1*jn|e23bTk5aR41dzDUQ=0@);xl!dKM3v5>9<%{D z;N3j`h-aQ{M64zo3${4LZ8}mzB&ggKt7u92M8V76I)9EMohlv~6#)WJ%LW^bAfWy8 z)p%TMQw=m`0k8q%kvd%!cwjR=b~#CPmzD3#5kS8fW<}(lS(eO&f*_yoV7)X8nxg4Y z0;m(1&`B}=ox{<+d3KdJnFS!L0B1eT0hC+kJypw#bFI#FzX;qO4aM6|Thy|hGYW%h zmcEgrhN)p;>~v-lHCVweouRD^hnKQh@o95Z79X-XibsIbRwAWuz`Bw56gLPmaQY=! z2V4Y})i94OY#gh(+RCPPG`wVPQ`{j)fHxwZM@Mjkf`JyC&lrxBt;S~K<$8iWvvLo! zwod`feOcHetI^D=LA0-ji6wy`1L+MEcNb-;G`eCw##FcJEj6G}&c#;6Cu7CSL#T7; zkumF^LFf&cvw~JY;wA6#L=qX z1)z!dB(Q$EDnVwb`MRa78Sa9b0TC#eAWr;Ek()$^)hbhh@-J%PgBZTTB!t(T{7wp} zdH&axPRLQMV77sO3bODnd`u}sCGbi z)L^QXRkK8~KsJZW402A@0x60QfOUf_yv(m~%LHpB2^t-J+XIyT6>V^RCzFGsl2wqr ziME(Z$$A(?un95djb8Q08ki6X)CozzMsAI1Du698lW*VeV2o2Q*}oWmBCO0&-0P5K&J4vH%qz-K2TpvPq34~Cw@hpwxpSa1nFTHhc)S_vPc8Y%7(L8Nq?OI1VNcZlokCNh zP&9DB_|-4x)FVK}0*(q`#jB@7!9lLugDOeRCpcn*NyvjB=!xYLHD!gA0(WMvE3ZH( z!jyQ|eBq`UTalL>w&t;1YlL~MA!Os{50a^}WIAYCuxNCxH=*wvH;2ywa8My#;bPM7+ z?7MC0>(Q){t!JdwJQN7SvR)UCc;c;~XX-YU;VXZdmaAc^x|Q{2$;V2qISPNM?;Gme zKh%>O#)|GoV~)Uvbp?SD^i45eb*B{l*)Ae*HeW3`d(H7|Mw&BhwQtK}StzBPBQ~0! ziJeKEUR@V5$nA(%&5p3(wyMTU9JDfBmRZ)Ne8BaXYL?`ZK-f2@v}N_gR4B$vGWj`L z&SuFSW`U)mU_plyEcz`m+Z$+H&a=2g} zVB4hNvpW_P*w95#&yFjGMr)^t$_1yVF4Z)-i&`K@uOgRCd_(3fwjs!9s|qS;0fn>? z??8Xqhlr<;l0^VE4b={fOK?paYT42nsqs#{hMqklA=&kVBU-t^FnRFyu^g#bUXC2# zTET<-Y_Ytv{pCDEls&s#LOM#&I}?*R5$s2T5M-zkv{zXq?C9mFS zmy-#iFtpym==SE@7kOq#)FkHV&|N#t!ijr=E_jA#VFOMo-5d4yN<9Tj0$9x`ODpAy zt{E+L_R>`D=6d6Fb~|2dkorAObGs{v_k#c>-bq9H4nkRXcU#wQN98cq@%N#&ypKNI zDr$Ol)-32S{YasqGzK%-Y2_HU_RAaMRJWDYb+H0o(X!MfDqnSF1DQSvejG1F#ab9F z%0lsGHN-ADqE?zY!a)N< z`raJ{VUD>gtl5|e!v%$snB|S61d=?*gw;f0Y<7jr;ZZe6HC?M>)jA)ft5U{eQWNot z-)ge@>QwEr#mGS;PUTF1Y52(K$0-i@x z);$;wb2xhqVSF$OL)Ec!tVpAr>(O+;OyqN)3?x!_XfX*+{H<@yIr}-#%}MQi|K20z z*QJ#H?Lid%47V_>n4l7G#uE}t#TnDuZ;GpxHplcJ4CcVJ183 z5ew7@g_c9Zh>S2x_?iJp?9(dy>k0%zeOw$Ji#i6Q^vyJ>mftiEQ+b0} zr*emMuJuC;+oN)e5*Gs;+~LJ}S7dgvo441+l;t3o@JB??1`IrA?l3Z#uRwdNxgOPR zy^Ll_EgL-fOnrj`&^U}dYC5-k0U;j#c$t`SBZu>W62BmMLjm{V^jTAgw2jB=qhn*t zQG#+1IQr_3qjMyF%&q^%sP=~GMb0tr=FA>&z6~E&`~&;Ap~*LBK(HT*!R$Z^sNCpB!WLe z8O`#mYwNb4U4Hi>ZPSnT)xl5g1xo+;C=r z*BH9!`E#J|Ku=)gY^ACaP~xW3YzpVC1sFB-v7k)mRqg`fJ=&?sdOCMKEe#TRk{C&1AsS2nOpRwq{&LC!NpxgyIRMMm0V3)mlha8WZ^XI-k9 zz`NYqu``6G95jk-MW^_4h2E^_1ela}un8{~=#7WV*!tdr>A6f%Ta9|z;A#+AAXqXb zY@^p2sRhNvv1Y&(2e@f*&FDuUeXuT#fSrB@K8dWlASjgPu*t6`mASIq<+mf|J!00YT8wtxB^w*dO_r>*Mpy z1co|H#83$+5XwVn?mF8LU)db0VZM09n?+yxM+j4prb!9nuQ-^}Jch7{1qD0-ComUa zMlA3oa!|hDgBKW@B;dK*Kulw+t1UC_tKh$QAt_yk7w1DP=AP=O4Fqowe$?o#i9T7- z5(A^s(pz72?f_L2o}mY5O+BTMLkvo}O4DegBvmh-KmWr&z4Y&o^}N7(k7u{BhD$ap z*@y6LlxTqe=PY^un%f(v$GTqdhUj&+EFmcDLuKBn)KdNH3OuV}Mzl~wCRHmxg=;bl zrG>>;CTIg1ufSHBD4BOxVc49f(1u3tZ#!yx=1;@t-bI1EPIf5x3)Nj5lCFAz5Nhk8 zD?X7B*6xNEU2wU&*1oP3eiK1W7tX4|xNY=#sbMJ6UN`s>nso9yvgl7Ve(#;T%&X9rK8_M?;0uLsZIfAEC^<46@g z)W-cF9Wgzso$?z5YRRuQs+DvRam{LH46g1=NkV=LP>?E|?^=V274g2WOb1|)gB!J8 zJh+bis6BLN*{{G}rRL!XVpMvQ_`7#M?3&v@HvdvMJy~(JQg8iRr1ta$JnSA}EBi9r z*rRS?9d!yZ@f+`U50=@FzRUeBcDPMUHov_qMB3P<$Y z$>6^(0Cvk3ik?Ozc7k7+Lm<^{v>;Lbp)IT)i{=$L4z#*aW1mvC-)y{dbVdarMo zw1J0ys#;Kw9wKKaBZKIG_fvM)i$_o*2FoV;rSH{468hj(%Vv1}*#5Fz+g*D-v4!gz z=_sxvq%cZxTdur?TJ~yA5Dvli#GaQ*#`hVhroPy|cyTFJ#R``ghf8GDg<9R0LCZ*Y z@BRDagbuHu#;VPaYmyTNvqwFEoCePHe2aZ{v-f{3aDYlFMqLGRR5;tQ;zGc0p*vYV zJLk(-LeoL^8sG6lsXE9hJK$O}h|WIb3#2pGJ3GO=09X792SUC{Wo#Xfm|BQ<`^S5` zUVVOc%0Ny^#KfeOl=$Z}j4`jCz4-kfzV+E14|k3F4153^mNX=UzY(g@Gp#e#%7|H# zyQqU$WcN<4p^kGe+PDdB5Dmyct-ayY(!3=M=BYk=0O z23JL)4yQQeswJ!0v#qa}#Uf9gq@cgK$Hd1hJBF%4)lsct6S>Ac+-^W1BmJIQibtYmsIk_pwRO?ojNt`W)SbbM zHyI4`;laOO;aDSg9v;0N{cD>1T zZ4*zQM)?fY;c7WHQTmX;7jTyldjW(pdbmPPhue8H%a+#?eNU-j;~~2*z@MaJVN1x9 zb*L2PA=LuFQyro@z!3*EI8EBVB8{RIqW-h5{eRNH^Zpeu5_qv!TF=378h_52}sf zI_{C0pbl=X2Ln6pjfg*3d_r*SN}KM1#y7dP`SNmgX4@=*Fl@POT<$bWpVFIWKY*n^ zbivfusBV9U*{mngUsr{ibiho}Sj6jW{7-&( z#Gcaot*^JE(&~?k67GMT;XzE(XUk;N_T;R0B3V!0HH{j64Gq3jDE`Q&`#gddNY(^MY41BppbAz zrS!}gR&#ZY8)pA+n9ZPJmpc*AjLmDaX>m2UO-e9P^x|WygX>KYwyKYdtjuwfnv&Ba z`pQx$0rXAnT@?9h5ExhB=sa*hD)!0N0j3eKAR07J%GXwAu#f**h$ImRkthh%N81|tC-?x0q>Mn^JP9|{5U}(E#~UATrpSXxXSt_XGq)~=2JW5&561+ zT5p7uW&;dQeWr>G)VI1gh1cN@L>GYNlMjr$I0_3&V%j+8h(YSu4< z;~vfdqub7T?`>Lp@2sdl!Td16mjsvXMYJf-mgD33q`*@B$WHKuGsB+kObQt>7?<_8 zSmwY{s|Qk24NKhj8v}7NLxD)XeYvT)310Bbs+-4JZqUell6G8VqhISU=5zhdTShuz z&bx^Jd~gn~_{XqGPp5}@8DD}CH0EJN7Ro<<4A5jdE0Kn)Y|1hc*1+g$_Y7lpjy}S* z!Jp5_EJD(m{ftuMLN$T_el(SYaO=6&A)C&YWsiTUzxt&_jA+7tPigaPUZ{PvK-~)k z&$E-4abFJoe?PhMH6*W5Np^ThdiL$M+fseCcNrmcQ&fSGN1UesW;oK0yy+5Tn9hflNCM(Dn4_%z&(Zh+?%w}XMxKaV;FQp)%k z1M{lZ4SrRtGzKT1mUqTue-TQHv)mw%L{fZmS$!RE61UOx1c{z@H$yJiiw+93j0ie;~#u4GK z5UZ~tsrz(apBmVvM7j%b9qe9Q=_`Hqjxmv+>{)}@;2gHk;N&F*u>$;m0U+i$kR=|n zZ!-?VCus75>q74YI5|@eb{ywhM1fSbz=0j(F3ESn(7NnOL52u;Rq<|5_i5X`Q_{Ua9WYGyE~LC{l7O%UsBlToi$ zsS*+3)CR!pVQY+Wdi+|r@W3{IzbsPzFV1VNk{2ZI6r}Bxd@zh}gXcen`3%uE3Roi; zxIIX(l`zrX35Z@ze^+|C=tsoe(2p$D5P&tDm(c~0E#2N0X;n}4&aS)Vd#L783F2er z9+Z-4?$uXz)C#*mQaRzci+-S+Z)99gIO0LfUcf(hBQ!I~Y0y=0#tWGiwlMhKCX;x3 z@T2Q{#XEp@V55)(GdJ$VUacM8O=q7+z3wWiRTl?{l@o`p#bFh0LmFFD>r5W!HhOnB z;VEu|YQgA%Xbn9^_i8CkkC-ypg|ypX4TalhL`WiFk`vI;*=6GSP(SDy!hLRXANV+o z^Qj9pb7Uiu>3Wxw`+1#nPaW@h&{)xUKrr6Uq-sZc1JP{?qAVisUR-xf`O=Zgd``u8 zVKmqmf4d$?=)n`<@dt;t@6@D6Js|9^(hX!C+?H*Q^xBSPp$HF*`|kF|&I`JEzK&6G zs_#|kv!D)`zBD;vrEGdWOvgPY6?j2m!n6a-VvV3*H^m1+-~$gSwci7}=h7x6 zfr}e(PW7rn{gLcj+V!Z)7_JqzCWN4aS7>Jr!O`7j3M-Cb`K$-`peTYz>vQXz1*XfFvBBvI)*@!iiEpC;XvjPB8+O&# znI5O7Qj#c50IGHm+ABBxlMLyBvTb2KG?JZfAE9m~{4dlPT=q@Ho_7{}v>P^bsWlV4 zxs3x$sV?HuTI(;};4JNsUz5xv@UG)J+t8w>i$61RPDE!uz8tI;-~`i?16kWH#e}|PPot!;;35vPC+}Ak} za~W$!W22)!lj!i(tG=6k8f}g0XB#StW_V`KI&GOIbL<%3HcR)ESHq%uSxa(A!|q?5 ziJvLHTv6gq)uyE`R=W`mA(}J?bYcd|P(Wvre;if-+obw=lhy8wHXft|)X}=s(K**S z4?2552%bwR-QAt)TAmK+ba~5mPJ+Z9^x^n z@1$~ia(0HkbURpR=enqtdz)v8^8ty28=14^uuDnD%_bz&JMh}Tt7SX5?IETcuFIrT z?hu`F)7-n%&+fbO9;W8|+bF+|Omf|A-`Zr+H4$tA_XNy=11xrSn@gaR`znGjjR?QArlE$JuXES99Lp3h0MlPLHJRYHVUc&OS7}vw^Q>EnHDfxnRHF>D&uuK=KQg~{Og zmvK_w=8EU(=jy-2QleSbxq@($fedJHTtN>PET(ZUI?KktE77<{S~zGho=uc|ONQX= zpmlAnvMPQrZk;DV9SL_I)Crku=+2E-jJMocq=2iaNENLgZ035D`t3F?2d^ z;A&@L9C7A6>BxP;`9`SoT4z5beeo5a(`zo+;Df{o@%Fq>N7Z9)baR*s-U`D)FIJ_# zV*;1|%Eq+k=!u2?gOIl<+i~P&^z!+Oe~jLtF)w=Q(eCAH;fyhC4^r^~cRryeJ)tN$ zb5_N?8ia#7g|LI#4um*Dij@YuN8zQSYE+NAXm+&tUtj!j@cjAUnASHm*H=T1Gm574EMzJ@P4DmX8 z$AW-Ssm3~AE=vII+q2~1;$Q6%@H5XY7w#=Eixqb%5Fkc6HkJ%Wqy=yOS{gwi3D5-O z$GYNy6CA9T^-na@x98`#?J;`OBBz{^9pd<0!)sxG2rkGOdJ`VzG(g zOh{TK9UJgR%HYhxxF`osyZ#-V)Th}J-KwempdX3x9y{5ocrsRV?Q_o!O59vZVwoyl zKY@6PY8$4@P?alFAey{h@}Si0loTLbSSUQ|MdRzua@N(n0})A+sr-; zN89}C>nG3$Hs%9VL1AvxAAzZH`GbaDI?7gX=Hmv4$F>-T#^Zw^7O54b248t;Zj0c9 zJ9##Cc5nAEuoa!39G>9q9jTk755h@L4GxF%;LzDURLvFTH>YboQeck z{c->F=&+5bvkxx8Q?Ms~1GfnJF;Ew$!LDh!td&MMP@{mJy9EjmY-w3}gL@LPOPQrb z3GrmG;pZQ~e8N_2gj~iU1qBee1<$40R$>B}z-pytHGwFrK&5nPi@nmCcIc@mojK($C?cA748axr$fep*X(0bRR!Im8$GeDZI zL4w9b*V|auXGZ59^_jA3>SNL0GjsYyUHD*_nS-B9BCYBu0p0Q3>M zjY1o4EkGkEG@rWiHss+oscfg)L#BsDEjFPV#3W64G3}}v=m)IKVtKYeOplF=q=G& z*)&<*)4f`nqXvq|@h&op$8H|5OM7akdcrMJZ|$zbL0@SnD~v@O5#DpYwj^3=z-8gF zbco1Yf!-WrCm6*K#jc24oOw{OVbGYCw{ zoj_EzZVA}`70ppTR>B!@9}&tPZ5AUxTub}IK~cQW1{}SlELQWe7eRko_8<%S=I&FxXnD%ZLHSxZWAKgwFPNg z8`!IB8+C9ks=dwK9_q_8*6R}-0p$vf)fQ+0%qhR9bZzzHn*>1mo`aU&gq>asZgTx* z=j2AFotO9VWh#+Xg%DuohjzX%IA6x85dD4rEHnX>y>k*hh#=U ziC~%)!>D6cQRd6Lj#8;bX5sy3KpayXpA3&kG zcQw!mFh5psp%)k|w`bcPv+c|a+Aq=0pJi%Q|BH1-VrEskXLH>!dh>z|$9g-uB!0?C|KjcR#QPdKN9lqe$B9MrGQxcp> zz5sV{N8HPAcM~D=2YXOObG%|B<|#1fS$L`9R^^4Z zyG86bbDwa^^O@$+YnFQr9_i|HWBs0-)KzcPsolhWjmPU%4^JhNw9XVCmgHT#LnTZ` zW%xa^p*fYg)dN|LT0sFAFM`;sL#1VYce;Q2m*Ly}^B<8pHLu81Ky=7yZ{Q>@KB)j1bTA#M45hr5h8nMQUY3ZdmJT5;-| ztm;u@1tN$>ak5$#v*apIW5hb#jlL`31`_K1;asW1gJ>Txm8^eNFNV|>$+(KQedq7R zhbwh@noshp2q8T2;D9fa`!yuGQ$vfem+ucKS@rv`x=1i@8p7d3;CgJAQbor;It0w9w-b&(GfGq-PbM_Xu)@vTGLgkZ|fBXn#vqOHvEoC?wnDejC{3s@!# zwbf;%I)}X9c@^zU--F<*r<~d>HR?aVsHf-D!^;l0L#`JR>rQb#-jY>72|HRpg#>KvBqvT5>nGDKvC%eq%Ku~+uqIdk7}LYQF>}j za1Cb>wMkYWJ6md?T(8I|bCCx7nHD{zV(v8B*-@v}xo!hXlpVYWS1jE^UgyMC+O}<9 zRt=(qVy;dmOZHAA1U1nM2pUNyO06W1kd_({=O5hN0CN%F%^&qS)y|7r!90u?Au;gveyy>cC4LLbR=;o?EZ#K5W6 z|8!f+e&)@MPY}f9C`&mrhdsK2dJ^22x*rq*QBj@ME=-v?{02lRLJ!k>Z_$$kRYFK|LG5*h3U#` zErSB#|5WX{ROrwkp%YP}kJ2r)#h^>(h~2G6x=#lLkDzdH!ooDg0|Y_m8%uar4-)_G-77B>)hXy2_VoO0mp)n4#% zL-k={DQ@_-z*%5EPD*>|w&%F30`*$!28cHv^Xw)og(jQK@6a4MXq(;(nF&OzLiYA* zo38%Jpl;_|fa%yzSI=s6Ugl&Qpe^Xy$!Dq zoF7LAFTefhd8fNZCFyPDUb_3UkC}?}cZtB8EPJz@Sp^R3gD1_KDTFKh;}kg52*75T~{HU=F2 zym#Spf7l4|D6<8KhN=NqJAYY#mgNW;ttr$gvDXjO9t6u0nnAbRQ0Q6*8F*aZx`*VR zt*6}xWrz!+OosMC?nfQ*%e!^%2qrx)B|SK~TtEVtGxF>0Y43}7oDb^%-?drrk3D^UD`(NyYWobb}N$`pg6Ya)!} z`WoL9%VV8cB^w74cf$um+7u2!8bF1bVDENJ^2ouHU@K0} z9;s$Jy;{d(Y6!(?=Lq9Lu%BosR!no5cTuCEGfq;6s4p0$;TVDyTqMS~%WUkjk znPo0WVO;|InQ#`@#r#g^u8fh|PPznDC=NwR#BYB*dGin z8F92GmO$K7P!bTL#v-|a(g0;_NYMAbrD+<08)D-w;+-9RxR0G$0NdHmvzj$J@W(hR zz)sEyUNINs3@bpvYH{S3&ILc4jB%K~ODVgA#mt^!d?QJ9n__stC=@sKbgJ0+b`)cT zQUTvRB&KcS*f&o^V4?hRCMlr2Alvldgls zRKMh4sm5HkSJ^yR<7E9{F+C6YRf+0Sn+c6~S93f{I`d)3KwtRKk8TL{rRk)pYETQh z%))2I5PEN*XfC+;|+5H$)}TAhq$pLVje+Tk!QQS95g0K$D1jb--B`%Zvc*(505F zR|8SmEeSHbpteS6d8aX%!1;8Psl7KZ@TiW*RJ-cmZ%f-VDLiw=U9W?1mx4& z)i&06)EU5wi*l|llJrAz<#2oB;_SSGVJj~iDK3hFwzguf&H~xGc(Q;B zGR+r@yQV>ekXL#mHVa8uF2Tki((S54jESCz;Z)~4K61)uxoq2ESmapz;*hHE?byn| z$QqU9vO8}WYMXOMw+O-JdBa;nL@Lq)ZqLURE&7n4!si2Vibiu)spLNj_dEVWoT~#? zE60RP6j?WQfvlYOEaDV!hLTOIRLycW=Ch{+uj$B>(BW~BwlO8Y^7qb_x|xr}mX_TA z=gZ%KFCo<~z0u5>2&bk7k&-A6=pRus%zKix;oTj4Imq3uhc@3W$U}LeiAea(s-2oL9ezBdC!KChOl5LD@kP>vC?nRS$ z=aYH!vzzAvf z3R2c%ij1+{t3z>U!ldK(G4pmbyHg5CV6e>M{*LrT zwOFW+2jkvyw=Px#E+Fefq8RAkLL!Q}YPcq>@9~gxjfe1Z^W7Z_5;o_?`@1aL&cf~m zuodVrmG<|9mN#M8z`ZM&4~<^W$89IOmabsQdv*lM#&);yV9na=RkD8i(28TSUwQS{ zhiI8ts1#M6_ff(2l=n90dR9?<=w673O14-7|4L(wXDx*u15 zV+qo{>?yWH2EtnPkFXKZAa$Kh7wDb?uCBKL_V`?o< zS{WbLY2zDc+G%>MPuWe+aec%Y^F@5V|HE0MbfR}t{Vx5n=H)6a78$-xfB7g}_q^m; z%{gO7yurcR7W(_*RENbV;G&7gCY+0>Ptiq6l3dhuwO&kJ^kND}cSDUuz)pOZ=Ovv;Txse?3`s2 zeFpikxjv5}Tz}U|dl?{zBXG9s`DQ~?4#{cVIlteOKg#R|(1;#o)cl-}-k@#ZH%;;` zMpg-GCI{xAo;`yc|i=pqXrv=z`lI>-0D1m9n9=ro*A{iN!RlL1VqFE4)=M;ca*xI zk+5jy)d1T`irh)66^5o3$Z4)nY)C4yx!5S&thz*@#WLWj!U0DsBC+jaTX+V)8&&)& z>um6=S;+2ZkJPp9)}>g;L8lJgW=27}lyrG*UUX7R;e%SHglWsbuNamj8fESk2NM&c zqMQUtcY5^0(VL^w{qv*Yckhm0fgpuyA63PM4@yt+lOW&u70bF&2AOoxw-3;faD9yX zI+%&x0WMBT9S2Pt_f?rJt`Uaxy112iHYkT~A@Br;d<>FIiyT5BJwLFa&Yocu5cQv16>#;RW@`0{Z1xk~>t?!~qIj5P22Xs~re4PCl*~ z@!*wU3_`V9fKSq(p)-bJ0AI)?Cxf(V&;)Z1{pzc)V1UqkGUUt;l=upf6{<4b#S~$R zBg<568~Pi=?CP&e!Vx(i9Ofyyk@X`Cz_1ZFyBN2tsG9)dpx@b(Cr|KSXph=6xeT4> z7zM(@)kkk29-Bo9`1Is+2-GRf&WfZ6pr^RS@`}$_=*O%!A4nRCA*Am)&rkQ?43FO&yn1(d1VB#!4m~*j;myhE(ZT-N5v>wzb-3uev*u#Dt>Kbq@4kC| zau_U(rB%@(?O#TXR7*j9d5iHOWlEKxKe75YK%Zj8papQmUYt~Eo-1LMfv0`77gJCo zIT}w~L(`N){U+}6-?9;j*bY}Kou@N!`i9g$Ft7vs265T-?3d=K76g$Gwegkbx*&KmNwFdNr<8o-5aPw~KgsZpMDM7z)=2(wUXe3Rsy(ot5 zNF9yxG~T6iuBz}u2D&Uk`YA~kRiMo@0H-wOLlpIJLXc(H=tu(wCuJHm-DMrs4`Lp? zV%e({c>vG20CYf$zYUaB@$m}aB$}#ehlEJa!B=Ng}N@;vGyp1yj~O*tMr1a9L0pIZf*be+C#O6C%kIP#0WQYMb0j zWse22nm%2}wtud-V-pg}@S#UqHfx=uQ9Q#3#!8fGmD2E`U9~dg!@_z{sZ-;!7w^Vl zYd^b60onu;q%iy!{pjWAzL!Qqeqz)62y9?#Y$L2GEVR{Wl0YC_g$;zdB)3l1blIu4V_yhqhp4gH2^f`juGXM=R zH#J1|Az?5jG_}~p!IOb@h^fuuOEZgoueo|whY*C6e8g{iFZg~W)Nmr+D56~IO_GX7 zj)F-Ay>&8+%eiG9L3+WbQG&r|6DbCtMoC7*YJIquOj}#gCZf%qxbWO>Cgs>VU0mApigXZDn(BZen$5Y-BHDVPk7$Ze%ZR zZ*FB`VPk7$Ze%WSd971TYuqppz57=@$iZHSP1xf?3Qg0NLYo%4&_Ys-ERSswSu)ai zyW3x1$!l+1dg-A8V{5+Ndo$xJeEo9&J^!dA8Bh6#nhdK(k=x<*AY@1Rwzx^I@Wu`& zr`nFOz1eQD?+BmBTZ5Vor0vj$JTQqqNg8K+lw~vW=%_5A`oTJeFvhZAWsOf}6Q2fU zS_qH*oK{pqK9i~al67~!&)#A&_u96Lt&cpksuthG>uzk{9wiMCFG&)qh4*-{hUS9V z{NC)61mPIb6K;{35R#rvH8p6-kP}RqC*52_iXn>Hp|Vzo@OMIcy2in4#Z*c@?yxUD zPoP3S?u&aR`A3M0z#HVRrjOlJJ)Y3kj^r~M>yXLOxzI#?+F&$v97rvH zn!rl_f8;hx!s+na6sy+6B`v~}rSH$B;@6aWAK2mo}tlw9<&YT(v! z005ot0RSTa003=eb8l{9b!lv5FJfV1Yh`X^FLrY>FflMMWf(FZ|#C1MfWf z9Nu~IMRYi?vnf0*7e$%Yc`^UwlSx_3qTz6|te0gr97g#Jp4Cx0pBFVXsXmdPMgUWN z&+DwLi(;xD7W&)U*)$m!vt*XfbNJW(IjhSoOAe+P4)p}SM`;xuoTw(tWj$^oh zzr^P!Jj6v>UrqB1HNNlQ&q4GZjM;qqA)k+n+X{bBV9Jc&9u?DRHlpb$2>Znnuu%#I z67};%I=as0<4S%uzc2IY*#B`hEiV1vM#XHAPqVTCVTOwZQ&7LFzt!{JClP#}!Mg)q z*l(7X2X9B&0*5U>S$~(=Vw#S!VLlm-^T{MD0jGoL20DhvLmWj~A|%*G`8?adDqxd+ z(pQ}q3kMlT^ygo*JM}!9-{fU6r#&TY4$MyjM#pdhgPo_dtcim%8>e;p$tU?FI!R9c zzwZu)``;ey|Ml?I^BtO|ypWcc)4Zz9nlSG6%FD`brg3V%saIa4^UGy=nSDa-zblKM zV4b5~>~*=AW~WtM4rI5`R|g;GpL{aTCQ-Vqi*Z)Ni^H30C@AdF6n6j+BPX4n(l5z3 z_}}w>wDl*wS(0z~3(w8KfN>O6atWa4H@*1#yjrHy<9a#Hi|@HXJcwQu^Q^Dl%jZ>{ zPN%S*70tCZJ_1@)i;HOOUD)aVEFNVE89n*U}?TCjK@z{3k zrH*dWGEXn2S!53($}4)k%Ie>o&12nN@DuNqS#(z{qjEW)~a|o-#Gd zO56>zbUwyKoh|A=$2{T9D1b*C4~!+M^7+UI`1o=12sbP|EoNDrL+=9UW(1R~0B6ZX z3aBWuY97;^fWE?V$iGK&4~<0zlEE)ZdX5W>4lwp4Oc z0hNCuf2qy>J^d(~&8%?-w0rJ8JJre|*6F zLhr0OzdwfN_s6dJ{c*kd{XRUuC!uq(Vt&DQ)|=mx(7Auob?%?6dG7zvGQY1LFu%|{ zYtHYh(EPsYn%`I9`T6_s5zen;RQ8s56ae{<>k=*qywn1>(oXd5H|2Z8qkVtyuie*X z{M|H5VI$KPr*MxF zvg-6Yi$=hOM#UtePNM?GSmtBE7r|SU*-c(7;rDq~CDHM9zQ9-KTSwF$dsre9QYFmZ zoyOZoZ-M=Re=Z)!=elF>chmMK`tjv=;kG|XQ#Cz74RBtk`~Yx^W569nn9>U-To?S9 z0G`Xby3OmW9)OJdJI<<(3XtgL%gme$=>geZME~iNo%6nax*6?N6-*gW2^>j%l@n?J ziO85-z?bQAw#dd2c1N3{fd2q9%zRJIIIk>l>0Bcb9#`3PV$UJIpwmKQa>p&GNYcdu zj|jjpgUqH?7Hl1TwoB-ZqtBuVA#b9=GeYJ3NqgRwm)Sf6X*3&Ng8Vl`x-mpyueTtk z9sfH7X)vRm30rc<1_XciP;5-_KVS0KS9>oHj=$U6KN!Aw{Q}mRh|tzewS~jd;NW|_ zgTEjM{I%cjCrg-Z+3WMTL?W2wqq3-qNj^_4@%0kpBk4S=;y%(Dd~sHQNI5C9zb|py`(OYC z*yu99$>z`_$)E6|pe2Eyet?5^RTNbgfTqEuRTMAEe3q7XF&xcNigH4ZVty*)rxyh* zkq|hYB?xre3z1UcSVM0WT!Pa(SZ$Euk*wpZ(jtbFeFX#*E=ttgz)(?71UqvAA}orwh^}DIq(~5ue9WV(^d>`quxF-Gu|#^35Uo8%&(Pn-s1i9JM5AR@18oRya=x{+pl)t1+94xU^&rs|oloy{jU7qn zfwtDUeR|w6u6;BNw7!sgdt{%Pp>-Z`7X^I=gxd`DJ+op?FAQl%@7f(CR-4{qB}X5^ zW=-$#T3HlTm7|PL*UoLmTSx6zzHR0p4v%VNdk_=2uoW&p?u{~o4trU0iKkPKR^ILp zq&whfZje!e>z)9@4$VNWzoQd4kbMMwS69St!44k7h_-Cf8l39sqjUHlu0$9UcwLdg z#HKH4G|ovM2zxNu-l%oV&RNaF-}{Uayv5U;UaL{7gcIKaXgy$HwqT>fVpK3; zIXwe?D)C-{KhyCz=bfoHOC$=oK0x6UB^;eTutKH)w+qx<-ejR;Xb~Q$6znksWyigST%i+3E}q3Lw*@JQ@B*J;i!JQLr5QVYm5Ak z9JqW;SMEJu)>K9glexA}lBX~hRu8)0Exn=o&-uBK6PS2K+(|V=$~XjefhP{=9n!ub z-eNa;_g-JS=4V(^r5Q!V9v*C9)d6O@oYv-b+O|FGCLN^<)Xqx}#JNKk2Ct|3)$>!0 zuJZ_Ydj*i?@`;aShtpP*jYxcLOzWb%vscyhNOM(qL>9S+Uz8>oTl8tV{;xFa<^&QL z`>g8q@!S%`-|RM43#V{2hr4l}WhC+B^KpKYkAVomkBefloZAu#h<9d7!85s zfKWT^p=cx9u2pA0z{_cfr)G!Pc7XUSh1W@+V zkbM5=^T&To9>?x0)pXu${y2H`DA3Nb;gH-0O?lStAVf|Wk{vF?W^~CpjT}=%2+A$n z%!WcXG*I(a6KJ)(80Tf>?l^kFclKC88)u79E42vlNPmTsetZY#?QT{m{y-QBWEtqj z!aEHvPTYWq$*ros1Nt=s0+(;W1wF|x727O#VLrbpt}_t;6~T*nkKnC*Q;;SCHWyv5 zFOO-LP(xyk$@r;vnuvpfZQ5^iksyA|{#n4DHt!%36lTl(28CR}oa|L$j~dFUqDt`e zBtI4TyeA0LVDNRtR|$lK>3t9NJf>qMQ)hELphH+x0U^E1gB?ak@?2gRpCENqpw&}C z%nAL35;>Y0;qD*9F*-O=_S}Kt{DvTGES7veTt;1U@#>mlMhU0Y-LMs6U=4r@=M^cy zfbr?cLX41Q%W2>0HPzY1drCnXf?jw%+uox8{l(9FBxkEgK zQ7VH5gy;PRtoBW-G-=eE`(nPvin~3pwbW|$5V|lU9fxnlRP45TDyUrU&U;z>Y16<)|7m{SJTJ{n6YeN(+DdpD z!WaftO6HCyN|QKs`Fbn326^o_$1dDW<1Esh!H3?^%ad}MH9=gYASR1A=k9xPjOm$K zHs2QHRzsL#cyr^8)DuLszmLM)>Xn?ZctDxs7ntB_5 zIehiqo0Gq646LQ@>&Nk5j^3Q$^LSwImF6tfM_=w5>CJ)5D1;KTf0d#W_-qiks|OA; zn*E1nc6{{a_5Q)|>EUaf9(89qTqQ=qNLw(l_teLN`OTZd7f%()<}lYlOeap9@$bKR z{rce5$#w&5L8SPDP)4Phae`$1E z;`Lf;lY$aUvJ~W+5(F%CXS_;L8b{0D?Nv4pfJ7?*DtACYLTjyMNcG#+F!N!0QTbGK z6|U~Vh2Up28xNwPI$UWrB&iJV_aQu*=JTu-?NUO6mz?($<%9Km6`(QC`Dn zch%g(twd|JtIoG*nGKa7#eXz#iFk?>7$fLNNK zBLL^=klE1Pu4~bS1ke!*yAsCzq-JYp9K{v}{=O_~x)b=2^Z=cK*@XZf@$Fy%{#pR5 zU9%pXJ#8X5oVNhPg$;@L!l;`|cf^ye96f_0hgytZ%U!KqAZUeJMS-q%?eLHw$~HG9 zHCr@5^A{^`eR*)D;H^2Xp!gl0ZI@P}OA%p~R#Z3ZfnT@l=34WLHc5vNB3G*!XlI*Z zEFCG^Kh8zj=NnVysac23X#Xl3UDGo5MChL0^`qrHdyBRz5X}{3MCTd2#74k#RbtD- z51LJ7%+4q+|0~=3y%+z0_G1!bxexL|x~Xh;D1{`}9-hT&47XI8T+y`K|&varJ<>ov#K6db2o?1Jap6TjQ~Nl;qASCX+!hJ5cZIKGq08lbmfGz?>a{shbeTnvd1HL z8Ge}KSm(&GiZOiVTqqxS=qxos_XhUHexDuO1RvVRfoXlDjjWORVLQ1wjr;EB76@&- z+QCT2wr+y`SvR*cENge0v50d(V@0%A02)kzeaMIzx=E+Yj8r%}JgXOLIC0;*&(Nb} zmj8q7Rv*}j4XV-4IJ;P0eqaX#!-ia!cYy`0(m8O&|M*~>4HY1}%Zyeyf))p5S(M$~ zTimNM<%U#`Ysyo6@RS*u0DT4m2|%`(6nrdtYnSU)R!+;a##kt8WBUrSmzH7VPJ>LCAP{9$TRhuP``2KAj9)sIZWbp zi$yl?xoGU1GY;$)E{|+JD$oMH8-uL6^#|W~y=lwlfH@^#vJKq4bCqIL0nCC9A#_@u@})JF_;L7&}cpSlMY6s-aPY3H7fH(Jv4StGgTW|s7-Q*1w_h0ybBga zr%ni|e5#(+QP|exZJDqsNVpMf=MT5Jy_)(}+M39Bb^_I5GhyM7D1CX~uR}Vt-Ch^8QYvtZ=MDMWq``A8& zN2p=GMRut!IX>z8V~itgjoYVMfKosD zGvjxZ(`eAc%9>%lqkMW#lD+wz8XJ6Nw-j`8I>!K;(Guw8O}6X10aEB3z}skBX1(aOzhd=k1r2)IOHwoIQ+T<%unyoXBt)tMv`(|jm$*i z^;_UQ#}-!4Q9=H^Tl{hbj5zRH2=Q6a9^FL8*j@jYiQq?Vp3||jwa3m2B+<5F`}J~u zFI2CCb%MY++U{mP98rw9VU&}sdx!m3E@b&u~h{oIyfHOV%A+XoYgxl`+)o|Cw z!0i=!DE#N*VQoaTI#m99pdd5?qz@h4q4mDZ((xS)6;8^$Py|W`-&7R9kd0e`kfaqJ z(#KvEyv*}4F#)nZ+QRE|3$k!U>qYL2fdSJUPN3J&{RP!+llaa2dX8?Yf<{uBS@-KZ z{`Y;{cV{S8MR(V&WmLpZ!_T#4`>jF)zv(On{}hla*6p)v%ljP%cq-?~J%k(2e^X7X z&3)5e2fCkhlCWfSmFDxFahwn`Fi>FqzuBEbe(gAXEjRRi$c2ML?`G^G7L2-wvIt$h zc90rjl5Wu$iY$`ENKz7j=sKTss2=%*2-5`Y6M6zMyVC&~7$L_q9@gTxE|((;+dAfB zGa$Ll_+KMsOw1XSoCq}NgGC9nGn*~Y`W{ObvfjqKPpkJE zefSCG#ew=S{c5NGz=J{oQ_!-^P=}L^#YNbf%O>T=h$b-Dnj%Z}lR2E1o(5vxz>`QK zvK&Tw+>l79g*b@Y@jcDmi=LCB^(K*-Qos3)a-GD`IJLIqc8IyBfL>%18ynI10gbnve5_C5FT)ee7!7MW>&oteh&5%W7db+2)=ZS9E->lua@I7X?@qWFKu z7TtJN)R|-{Liw4L{n0l^FC$E90c*@+^#v^bb%g6M;2VXsd#=G#rQ18srnEDnNt#bB zr_#;nhrGU$Toc&EAo~9J`(tiKmSyrw%`uYR^k?2T<_v$1VUxvHm0!ZqN-H7U4nCdD zb2fjeTwlC79m}qf)SJ&JpOT*Grxk2AI7h)K&VGA;n_e#sI9`;e3+jWe6SeyQ2<|_< z=R-X2hnQ8T=^dgD&0zo8Xf=KJS)S8Z1XYs&|g|21HQR|<>GgP7P=-p?@A0{Qpb#~XP znz6>JV6?Wo0BQ637oh$W6CNh}jBd4L{O^+o(|GMzOnF=1c>>IiJb&MKk9B;SBMdt} z>&Ge>Imlpk*>)&pg{3MEJ27`%RDN3$kWkVy3S)PeO3mG>CgBB;^a9SE(Z0VLw+r-i zuGPV=D2+E&a67HZH^^~o**=X={QAwS;mN`A$uU~W^dE@?a>i?dX&)TT2M5q!n;nQ6voj7Tgs-GnL8`DZZk^J{rX`4=+(2s=Wkvgz}=j~ z>vsd%B=h1niQulyB9#gNRy=k*G@2xht^W9IsIn$l9dx#z1YXdKu#P?_)HUE>-A#42}gk)G~g;JPpm z%%E0mGUv5^=X%2KaF+Cb>n|JYVas1sb?f@8D@NrL>~S)b{>bjB`Z-q(WsiiGEZL(Z zW&4xMdo1EQ)N%aW^W+18B;E80Z1cpu) zA?vG4J)&%`(>qF7H6L?It8zJ~7*3YVtE|KTmuWuAQFY6yYS<{wnb$&-?phVH*d~wG zUz49_T^_zRo6$Z9Jhf!4Q=+=F!vlgZJJ#l?`v>vF&3~YsRD%NX;`H-#TqvhbL_%T2 zVcuGfj^$+@@IAkW)ujbqE2Pb6Z#-6RAS#)uPJ~2R1hF`RU@j<(Je(ll=OYsZ{PY$e zPquH7HTIkJs)x{)An(oS5S>WlQgX%Uv{P!!k!s4(>BNFIqDPb!OSF-0y=|jYqH0-bjm>lG3N8c znUPNsTH2!JA{rNZ=E#@3$+My)Nn&Gux=rV-=!-R$7;;O$AP3=59s^~&EEqu(Qn9Sj zDQ!zky-Ci|lt`RPblhf2vTf-UPKLXnAn?{OQ9+VQJeso-l&vec*?~N(dsR|#>Pb>g zm5aNU4rMrWvYh>rP|YTy@zC1@>mOkv#w%dAGd`v11jNGIv_w5}UgL&~d}@iRyhp6}3L81#vuM#hbiYLwCSxlNY4K2pw~hyt`h4_@ z4?iDHd+@V1{Q(|M={pF2mbCPr$k%C2%mexKd+~I?+_Y?2^yi-0N0m*KYymRLA}i}W zBY~t#=xiYMTOdxV^{ZlxiiT%6OPlik$?~yCNs=8)N?Z7 zXa}Q!Nl7Wib!cc%Ep7%9SyX22j6G#Gm2NCR+uXNjjV^v7v$u-3Vbr!Y9by@l&iyR? zv5pyGxT-~?U@@VuL&1Qy(^B0Qdtm*6>D@k)jY~y?Z*C}cTvUq7p3?qLO@OO(AX^ku z&!~{^z8xU-oj5^w@`gwlB2uUyvCmJeAzB-Ir-5G@AaERTU-upvpO$1%EPB?9_He`x zPBk?ojcY~@Gl#G9EP>h)%C{<7<3`*feZE-TXkb|7$xTBEWz)LD$zIg0u z+=g#v{a6hHe^@oCX?{_rX2c+BTFm5o0_#dk^6;aAxT# z1fCYUiDb!^+c3du(VS0>l3qLUZCJ#3hKU8RI7Z@u{2^n@gDU^xyh&jpB^(v1xD5=M z>e)%R?Lkz89@|^vnx*q)a{xhE34_p-3e5?zmz7N0Xgx5G=IJIby5HVGd-UryBMF~e_ zVM0kGAE|epJ3yR3ez!uOgT57%5v02rDT%t9pxzEOLA&uAwjHkWn2>S8!P#w3MC!Lz z3Z`{lK^7eLS3b$Ax+8;*9Muir=OoJ~l5COi$*Z|!_W@$-Z&%OMET!zU$*57e8ekHLH@VcqiH*#qh?u4k+;n1~92i9y?Kt)u`8g`R!~v$4=v` zx?b#TZ&#zM3?`fCEPUu6x;<7I6Sij+F(KPe9zFUzesA&bq{l^>di8aOvwo3YWK--n zky%luKG`39ww+E8rq+UoC)hKj{h*{YoD;bF@5Mfnr9 zYOJDO-$8iI*4%RMV&Pii9czFj2T2Q1tuW zU~I{m^>#+exTL?sg%o7_i~9Ob9RWH3yW*`?SxbksH=9x#O&5^WiCKf^&^qvPgU7=M zy4CgV3gCau?rwqKvH)M-Dv)kE#?N>pmh=!l{8U97rY*zXE#u$zzTX$3hf1OTnT~!) z_7{sp$Reg7TU4>5x7ZK5R?vQyDogiU18@Jd%*2B4@3|97cBc@eZpMaC=GVj8>DZWV zZ)$U%@QbFeAY2<)#FWj@d=7Lxi}s$K9K2?O_plv={aIJE&fiVyZavM%9O_UgurSskpaGFs4KK@9BE&s;+yCHg zjpq4cek>4T=khgdQpa{}(0+eJ&D%jwyYWP$Vba6{@kSgyE!iKdvv+!k?*%h1%zooQ zU^q>Fv8*$y)dm|BvoI@7Cvwsi6($(x6`ZX*RQ6Pn&Kykzsx3bYcyyB)7rDH&6sglp zz_BgI$oZnyUW3BFf|j<10N?bkDAIBJ3{%5T@VhDWafdEHa;9mL=p{4HuskeQp2Q;- ztNL}Z6tGj%Atf2P;*=R{Z1_NvD-1gVdLsCbKmHi~kW#K3imDUBK}GB5qIyFw5=(m? zv0q7*AuEZ+YX9uOoHzMP7i+I!rz6U$MLNnnA#PrEB973zYEd+s;J((zG}w*QJ+Pvp zVN@#T#om5;mcPY7+1v1zvTZP3oTEDin9b4TN)iIrar+uy1NbI8wk3@)X z3o;*`G3|x|y508#9=tk~t}*;mFKq*W%M%0&fCj979i1L6>!*##cd->JL?vfuUbedV z3aE$k091ZUA0g3EAa9BAT}dxUI1PM02OiDTFRncPCbKj&Ty+rrUg08bNDuIbzmH%O zN4IcI04q@^SWzx(CimS`&(P_wydx8ZRS}u;gpsKh9XNE@>NrvQ559VgrnL5;le?zL zRF;VHuE^K~az%0_wZdRgyk8@_S-X^`1|0{pi){Q-hT>BPho^|-*y?ekg(Ab(RbEgz zi|cf&(t~vM>~!Yz;9C^r+*-uK^|Kh3%ef+@it5-JxgFn45L0(pf&M6wwdKqW^sNqd zP!lMtc%QJZ=0}~oT~FEjaELg?-8FQHT_9|wo>l_ezXmFZTJ`P1@wBG=ol+Tx)lG@y z5%v$Kk`YqO5|d-14~~;%>U5jaV7==(fxM%lbrUQ+SUY?jv&r=jz5 ziQ7CU(9urwl)B*xE}OdWpk1)cRBuDq+aUXpv$kgN-1D1OXrXUUrniT4I|(S&0Y8^v(67+W&4)A}Y*?3r{w_1&VlTL?jX&aR_ zIs`+Co(2LlHd;i9NQvE$lgA-JMdQcR6O zJ}SyPD^^(u?5{;r(D1bDY%f=i%S0nVc6-WR$ATD17uKn)>|`Oj(yj$c1-a9S1?moK zXdZtgiCu=`SNK7^a=?-?oKYu%cD8v>?gww5Qd77TK<>UDnNNb>X$6ygk-7;WwAuB;hSQ_ITcq)xRx4lz;+gy^e zD5}SyN`Zz{((Ie6995E+jg&n^?)z)sGk)pa`?D`TgpXLSbDcV!KXZLfPPKR;*-@-> zLP@4`@&J%($rgn)?|<;k{oq?0*I}w|A>suCJMbZZ*Q*Y?;$z`;*ZTe-Sh^6YJ_J&V zRAF?zI;1N;6Ceh=c-max`16NCUb_J5ijQH(l83TmiWSqFagNoa`+>WYcRiCi^sx8S zi*X?m(iUiC0VxIyK~)Zo_ND}pf2){mVZusRUNEbRm9p8^ZNX-T(A6ZM8iABPx@z-c zU-^k)fW?G{M{4T6UL_WFW z_jfJ%w7CP?%$FaVb6umjtGb+8_}PC)zLynjYcamu^#5tjSGOVspT%lu{ajN&@++6D z2lX%Hk{{lOUfNy!_6tx8v;^0e^~255Ck>FILGCh0TfGM7aUaw2`@pR9Ml^9id>?uw zq%N#%YR+U#(E*w=dnpPC$W4SDgajP6ji~)wK!Y%8#*kPM| zZiR}BhShR1$=~|HCo1Z}DgYXMeX&>tV6o^1LGx2E#HaK;42zx_Geo%bz<#j=(G6LE z%VkzoI$(rF3p-rp9pLJHVy7V{$K%nJ+-LAjg;lvlgp_Bi{GnZA;qUW76#Df%ARpnt zD2!q_nuhN|3KGI-WosZ!)gwr(G-#{Uzy;oLlm5l#xO$oks)**rb5LV431_lV#@|vT zknQI&y3*Dw&My=<4=qlQ6#?sDM`7~cVfjA_Uf$T2L5`);;{CeVvEZuG_U!|EhGODa zJXonLQyhe#Mkr(YEQ-JWwAXyOA2-gX4EUGYy`hDI<~vQSDzQ!nJ6nU}OuSAKbv2cH zEb=pws(>x)eVrUWnij?N zav`2Y`D`I^Mk!VB0wOd+c@(AMNlq3#8wzxdPoO8KR#p&YUp#?;m(G0Vk?2#9l3c`}!hj#2>F|o7?7NEj@ktc#t za?10N)7H<}H5a2;L9kp{HB~mFUY1-Y96P3ROPGk$zKdcQWET#L%QBrkP3!a{R--U;EP?-uX48NJSC#Z6|k*X+zl z#X3@~jBE*BSh5!`@f(}TZ_V)Bd`4)w%x(Noysv>Z(Bd0+)=-hsVb)(fNU1Uwf62R7o zUK`IFjj?VRzbB>Oq;=r6+>DNaCc$msCgO8MhhDt@LKIw77KXZIr!g3OS%CP52`@nw zgg?-=q$mgW1;lljl)D z=8$YmfV?9LIBHMEI%+&br7Q_z__~e@;#4>Ttsi5RolP~K(O72-xGd)|%YjZaaeGyu zGXZ+A(Pb|MN&qi2bfbom@Iy|uIxEm4ucQi1CRblZc`ya-*t%lUBG38S+*U~dtx=^T zz~5MW0x1YX%qucCIz%xHYylGZ2rdF)d9+uNjMXe2dp*!Ds+@(af-CvzB`gZ;lx#}n zyj((QauU|G!>y-w{>YS&loOZ0{)`HFoL?-*h!qrdiAL82MpE2a*A_1EHjZ#xC=sVx z#H5s*xe;(9ui~DbDzr|PQ@Fsy#$t_4mc!bl(7IByR5=)trqktn!I17SEFh&!xnyDG z2oPNPZ8rm~g$Do9*omU!-= zX+`Kj#3Wad*dZzm34%$}-0TfjYKD#~0?-)L*2l3B*HU?HA}y-mLL*yxn?+bB5g8O> z7ZLx^yKMRDiq`kSF8o710q%LSHZSkb?9Wm3l4=tm$Aq&3IDkV=GfB4!c4b)G3+yn( z#2pt|eVYNt5Ja z>N=!5T948POWRm(oDTib#2Y8g#~zkGuG`3{xpfke-`iVp2hDpwDL`C}6Ylmh}$4wHGP7uckee z%~;~U!bgB#?J&!%w^W9I{cVqUjh2S}uDF1EELWpHX>**V<>-p6Ll5amt(6TvAXxYr zip*4p?2Nif$dXpp3721*q82~U(w4O2!yL#CLycJ?YF&aRT@k2C&f!1Tk&e68O z76o`cDXC@rmaO!($Vv*G?z&hCu2t)SS`ut#FGx;l=$yxl^?QnV&D|TC0d_7nH`XP2 zMz@tnHQo!3HW+0`c!s$`s-5laSv8(}nL@Jp*0S1`^laM~7oY#(^FKWPW0F>jx0coO zf)v95Iza+Netf-m@u!#%u{QV33{ulcG#Tr>nHsTF>jWi%%(`%o9gWvzm z-=E?4-!bUD)!-ZtRK{6^2~DeMT3u;(%Dj&8$_6%ezQv8kZK>B?*c^_j)WEV5Y{Q~- z{;1Z=v5;QNrBz{$?Ww>M0s7quUa$`pkG+KgifbduDe(6t3^ z!&qqQtVD64&XVYuq(SKkMnr{HQ{)*C%qT^}Lv$F=@I4FIbw0+K$sUIPp)5vFsOUsd zURSD~iW)3duu*5rQN9XZ5f`l9giY;n#e%%gb~*CY+wYTj;~lqnzY%xsa!y%F3G{G4 zJx>d|v0?H}9Ai@uKvY>23fD9!2U!wTJXnYUTUhq--$k<9K|Vt+#p5c?W8<^glVaOe z>C)dxV42%(aHLb^{BEaB%`g1yssyf0#fyk&uo>{9qjlbXarn)5dne!Sgi?{Kd~op6 z=^uBRQpaZWG}{_47eVZxo}xrU09E#wqGkZq#Chg9y&GU48D_U+NWpp-oKQqbjV5b? zx(SwZES}6b2mo`2g4OPowM!}7I3pcb$>GuGO-irez$v6emwZ}bm>AL8D^Vx1g$l>M z-5=-lZPI&`q*eX(X`uMJgZ3&Ruv|%|?{R)mv^@Z={G@v7d??KZMCv z75gSdQ%1!#*Q3GAER|p`S2{8j>@m!6oR(uF$yAg(z)wMWf>s_PUYCVBc`Ci+)H@)LMZCOj_^DZ9YmOGHmb%v8VHL+^!* z7ge;pSpZ`d=8krj@G_urZ|Tl*K*TOIef%JlFK{r(Yzn;;CPrcuQkj0 za(nkG-8lyNJ!gwl|0g&ilX};m^t{5JWLOroXDk%hCvhT4UIpHZ3@6I#U?6 zev_4Dj_e0GTzV?4%0XN32QIs=X44i#TCy z^^}64g=D0=d^lndRfG{HND^8|XJ_B^S9{w z7JDgdFEbA$-p2%`1wt_)Cl|9f98Yggt>W)vUNUFI;t8bBybIh}l$KMW>PrCbEEYbI zJvXE&`}em$vcm=v-ElNW>9zNdmvek5buhC#N%m(pPNHwo>ORo^ zBqjRO5wFUceG4X*?diHIP^=o(TGZtB-?!b=f5uCr_*XrQgiRH1prsX9Tr z>u~~&J=3lI0V#{qTRKS&#tHup@=&;PcU|7KSEb|=tbF3Q`XyIjvlms$c=Kbm1uH+X zqy4d6jfKiMqi#y5Jd1vxwp=;tOimJjPPe7hRp}{=7{-r5Z^}&CU}vO0ioVBN@SvF& z(?y&*u{qg%EZ@*FzX_K!Ux651<4U2Teb0;&$(f?^*|aETAGl>6ir*lDd5g91N`nZ* zjkkm7ZzC3;K`M;5pFTS}iqHQ>ESiL{I2_tMOH1MP#wHEAkdcbLS;r0^mziQ~sE+nH zG{f4ex<=-s+#%VTV`y}2O@x@B zByQ-(+>Tc8O9tM#EugV+wdv^Y>VKSicc>^5ZbnS9w!#XoLfV*?9f- z>vlIw9+p#G+v({}pz>D8rndDIIqU7GabKCB?(fD${aa^9S^rYsykrpclzgTwe}Y+B zUI%QUX4nrK7Jig>8bS$QB|)oK==IYl0ZX#XC~TIK2?!9e&bcmi9fhd9hFy+wJdxlM zAJP&ZHkP(n{U;ETP<~IQ2wb6%?;5GP11am z03&{zQ2+t+h#SufeEPH)5nb94-mm{mC@F#3D6+U*;u;y10Vf zjG|B|)acEt!yk<@fnM}=E)_*1)tRJkCcDH$(BWULW6GKo{RU1Bn($_ekgu(y?X{vR zlI1*q%hoRwNXgnwM?c)cl>LV04Gftput;3pl%YozEkGEhnd7? zX6B^4T!A|FuB2`({~G%nZQk&Cw@FtiqU!PwzaP%izJI~EUrNh|+4E+(Jfx@l*F?S} z@t<09v9Y>%Fu8c#n?6&1x80na|6cZ3zJl@uWP`vZ60#=8FpMx3RE<=$CtZA=0d*I+8zjzaS(J6o^2>fzPUhi* zlqNtEH*kQ3UVUFCo%fTfy4xxQe+C@wF0$BC3lYJv5(A{XQ^!XE%e>GWyikEE(J0So zrhJgHa7&5aEv%TMJsU4fQHBf4DWOKPB}W!}mbp5l)CZe$?x>2gsJd&MKyF{mcLw7q zrN9FaNX{9-z&I+K^_Hk^h2x5}a|}QuQ%|K^g%tIB@LTIe7aFphgeuSQRvUJnO z94k_kf@io8%=<$ZD{(sa@)Vvrlq*Fh`a8I&lRQm88pb}ZaIp&D-3h>E8-cW29cBgW ze^9T#{tdZv)bl*(YH9-(e$=X)nfqvgwskn=KeR?t=ex{s6bIP#C;V17;a?VenjkW~ z53qt20%N5gX&#P!UAT;X{K zhB|NLd1&D30`m`b+9T799SSw+W}X%fnTUb!Bv9xrW!+nJjc6#q@v$7hWnwv@g8ZbU zuD^sh`F_kLm4!815!qc55S!dQ_wnI1itG}$&`a#r9Khz=bQoZ$**1AS+nZ#u;SB>s zuNil(*Jdb*mg}pUuD6FyyS0*MB%F5g?aP>>VTj-KporJ34;#2dVz7wNXm86AHM&;prv8!bq|)wDQB9Zt^ZKR{JZ9{i$N;ID6#5C|4I z3ysN@uhHN=r-7plU%tig<)+lJ#nk>`s4FuO@1&_0<=lm7u)Wzq!R2{Q9L4Ix?#nmq zDuQ=VZcukeCJ{y;x#)Y3W{sA4HJUm8efxW(@+V`SS?ZKLQVFTMp-o*iO@kxwOVlf% zkSPk%irp=OH*mZ^)gfv5ue!uKy>0*mTc zuZ`eu=s zcHlBCnnTiVTvqD8;Lj$Jbh)}22WKT7k0;>>FQVOXKCW(Wz-}BG;)!MAyO?gby<8?7 zS4A0vvbMg2?emHvPajUT#D{$39|9|bA)Am|?oQ?1lFv)i*)Z7o@T(^j!R~2UC{GAy4Xy~pR>xY0B+SYNET%u9)cqhOzJeE~t(;Z$5tAt9BRDVf z!r4U~d(0&qdOEFb`_Ikd4VWfs(^TV5-hchHeMcFb>AOKlbN6wA-aR1y=$eb^)D1CZuOe54Cni0YRLa7qM(lsOe(NLP3KtyUWl_;#PNPd9juIFjSG4^Gb(qRZ>dHzl zbOZ&^)!6grm3w!cwPnGbVMRLPo$$JcaqPWHU7^^t;k8OHLlP#bu&)Bg^-85s5Z?Nk ztKe;0d6(*n*Swn#YjG_Sj^Ce^3XlnwAUP<>8E;yVw~wDTMO}8ShSyhoo9Z3ivElJ>Q$6ZNpEmL6 zgnzPCif_KS1}S>nxS-c|0AdVas-4S9Qz9`{D;^751}Hiy&}kS`#a$=4V2HKlOQ>Q- z2ZOFD!I?|TM7GW5>;x)2#L?uG@i{m=JrL|FiYE}j1zo5oNTNJa&+w^$*|L$B2+E(@ z%92(fyImjv0``X}o$edw#sibN_oD~M@yyfrOr5n`nwxO$_6S@$CTyyZz{T&PCJvw% z?;iZUzBWC2keBq09|6aANw-~TC5LWfb=L92g|)4UGj^S3>MNbHh2%wq+=<36^ZcMR zLK>Wy=g{g$U=ao_V8@elXFtdt0KJMq*UXCru_*hR|N<1(}~sFgY08qU8ok zCmH^{EZ%9|Isg8V{|%QSJ@f5*sF(OuiFtktOq$m1poTshtrcaBRm(rBsSYMYQCzAM zLO;zjS#O3S69ZGtzh!n2N>>RIt_aM33pd$MEe=e5jQ1a@wFm5;6FxjKewPHezl4pA zR=y;I@Hdq4cMEkHg|F2yB?Q8GZN zEIuoZ2)pvz>DZ`U*EQUri+Jg9IEe}8hKaaUzIF$OR>x3RcxTJLbi2b7R1^@)!!5f- ztx6m|VNni*+&1{&N6q61Kl&i_ESN{3-Jkt_fSB=~e}HLC3}`$f)W>)AaSoUgXQ z9V+>qg$k8M9aMU)Ry&o)pqbNKxzpFk7j<6(^eW-E-nZOKwx2V#fF>=jcxY4#M1jS6 zcWWze;*`6xW}b<6se7BVIMZ0#<%yrw33Kv6L{2rRr?FI4Q;m|X=9!b@gMy@nk}}xA zx{daoHLT84D;lpCingFCBhQgS#ajW+W@i++o4PO_;hve9%I?Z+@Y}gEuQv+ytYkQG z_;Y*YB%ra>Rq0k6Y?mxDNdf;53UE*K5F50e@`8)d(}T)j0JVH zan)9?wR4%nq%?vVMz6IB+d4L&oFT%qFCdhf|pDQ!#sH&TKC`Eb&`Hp(}_@DN$XVxzI8Fo&B;T8)9d+)6}L)C(F~DlYaxEW z2Zf!l;)Df$OB7TrMcY;=Xuu~|I21!>WN1B}tOk@njz@lOG2L}O3D`o9LCkwQrlHrOc5B^cB0o^CLh?T98j1t9A;*-+fpJWGys8wNh;#QeD;D27Rd{b{mSB z8>kt_o%-#VyLG)*1iH>W5tY>3@PB{IU1FX~b}heo&?t&-<6u#KNA;*5asmLl+wtzC z4wUY&7P~j@nSbW;&6rnR^phK~iambZ{pUHRzKH=? zIl31jT^ldDqxDHqZRUP(fbFK``vpax451e(HJK+O_hkBZ6lY$=!mev<6)wgFk&9}O8YObM?aTBAx&<4w3+6wq`kPKnbibR`d>CNINK z#ceX47*u60D~^O^4>A@7B!SXQmaiUaX~0>ryjFDn8;g}->Q=@tO*J|*#=l8CXz8xx zz&ndw(Aon!lJqghtrBIJ@2aOW^4@#WW)oKO@P8hfZUD|-d)H5yrR@&G+SW|2xV~>1 zGxuzur7F$N53SSdE-t=BR?Zs(c&PyPZFEVtE(Sl1 zo`;VlB+FY*u26hnyG$2k#p_Ut-B|!7j9x_v6^cM#vs<({oZ$6*sPF5Q-o>M92-ySC z;r=$)IeIjTim-BJ_*U29o~GJ5TcK_X{M^CCG1Q*K!N7Kz*)-w0IXL)s=gcyqF1XkO zfKF4p@upw9ImUnhMVGU!QrgRro!~*T{+qkDl-k;U`R@cWsvu6emcZ? zu@p<4a^OxF;pfZqHo`3&Sj{p=p0dPhB5gwLy3@OFysNScpilImog}<~)D9UD8IuUb zLL+ZX%K9Ev#C@%Ev)ZEyGPj1sP&B$1Id_%SI52yV1f990%BWT!O;QtB5}9tQzrIzt z>z_32$}$bWL8wFnf;LbFF=QvgSh0LdDR$Yr-@X~dP;bWnv~)-LLkoHf0L)Bd62710 z)e(m4wKu2NpX5aYj503wa{{~9Clf_RT5KWtBl*NFu$LoK&ijON;1Jf>PaQ|>oxH-A zgqt;>IKpY(IYnMdm+U7TrcBX5>s<15W~W70P<_4|R}o4{2lmHcoG>6Iu-EofrFp92 zdbHQ?GZYMDLTQ!RDhN5oJ8oT&&d}kVPOta`st;TZVDsOYkO)YyGwMrI47A3qQjaLLC`$yW$L=K zmf6Sm*uc*yT{-Vdl!9o&Lw`lkl8s=O3GRrT#BI_m%kTC?#mp}ks=aP@oUrSIReZ#6 zEDdp`m?ViS&9Q2*8NUuWP=^;C-UoQAsqGE&?pRe8B3Hq}GFdBO^QmdN5&wR;XhiBw zZ)+wq163t6ow@FSer`}U6G{bpwJGNci0}|{L9-kCkzo^`VH69OM>?>QGPvLL_B`bQ zKz!Se=iBJvOyS|^k&pLVsLYUt4rbim=ppW~p+vhx6TK#BqKl$^M}btZ)p0&iEVegl zhk9fziWl-Fz>}B)+#j^PWHEn9?pndw<0mmCzFR=GL3f6N2C_IB&=Aon34m!7WJD8) zfJ!I@7ASbPkYZuaCFydmE8J_4}K$1WFCW zp|cru^MY!w&jd@*Bx);lOYk{`XroK*WMKVfRaM?ymnve&^CexA_g@oCp!3XY_$X`h z`MMgO+oJ+dYZxHpo0}>V4HFoeNOV&ljO0WM%|SiK0}gE_=0jI7E2fR_{mnvjaIf0e zUY^fyeKSQA>eMnrSlGlN&OmGJXEcG3={2QPey5_$d5M4zm!nks!A;eLN%>>ao|Um} zS?^5XVqeyALk>D(KzOrJ#eKzcIJAkRX+;hs*1vTa81+IpKjvbJ0CLIEH7DBA(UyYs zz=US4_a5TFw7DRZi>#ti@unOp0yC5mpc;m-oqyO>q%DQ05Hrg?Q2XzcCLLhp1)`=X zNP@i7(YrU6T&(#O#vW)LL2-AMr~wekE%3ql_rEp`!c~Yp(_=R=p+6BII`K+G1Kp3y z1>oyu0U3xc7UB(;WV(8!C#}ls$hS6eOGu?l&(xessVe)?6zjPG2{<|DsH0|PL$~D1 z7_)NXo%7KlK{`a=ivEzEl)6Z6*@=oJX)V|{PdoiI$@~Q1Gbd2S$fDHnGW9TOkvA(I z+9iNdLL};)HRK$zV6tNz%*-Z0Yny}$ehf0pCb@}PmH0Xa(g)G#a3U{fJ3 zVOdrGkyHG;=PDBhl_n*VEFtF*CHcc-NT8{mVys@qbnjn2N8VN2LLLUye@PinouyWV zj=Vkfo6Qv%cBIV(yE{`8?n@l>{$_{@L3z`j0-+TUriNvoN3OIVqCX?lU7oao4@{Vu z`qw}b$TNTdhIpN`449%xK}bBi34jVmm~oRi$|iC)Y@j*H8W(!b(0ktX4|VR0-2&wx zt!}^LKZ%VI^vVS~;<*t?1Tndx>8ffwRs8NFQCn_-MK##uzy;@@iZL;bhyRriBjhVb zc?DUheQd=@W82uUYI0)PCADS^ng?#N1EA@iIm_pV6bdlOObrk9{xQnueY}1KEYA=O zr^Rb&jBiF6x^(C0z3sI5Q4G%Y0ai!#Av?M5bJFR6CZs8`%k}j zGMqhakIt6~y#rFZg$%i2h#2=qxXk??ZCr8Rb~e(&!GgFn#fwUR_SRl{N8AnMveDJUyTP9A<+T98xGM34~NOuM+@HOHGwe=MkZ;KqnhFMsv*T*pV5i_h(v-1n&7H=9HgXj!PWTVPaqN8 z@KGTPQBjjv+K0#jxD&tgZ|;UMdF__6HU@&v)Ovx!y+nW}PR=mJGK=I%6D*il8cPZ}w>7~PA-B%!e+)V1l zS8JSRNi%IFR)LZ!?v;Eyvpy=~XTqf+aslNNb2m1wI3>r0IP{dybvwQ?S9v9o&^YT< zGLR4vj8w}#RPwEf58|!Ku`rbCFv26feaQ|Rg`mEHOe-1w-e;(Z!H(x1j(G*zE5J)o zC?ySaWH`qYQfba=CWJ^~Mj<0QhHXq&&O<(|&XJo=hTz8L`slF$=B5p0;yW5VX}Fy$ z$+@wvcD|$DeUPTdmLWgB`n*k{$AX<=SRIREr>X^x;e)E(c~J7TgV3S zkA9O+!KCIM)&8bG&#`G=^xO@8Dx4fX`dDUxZkP<dSj0lrpA3XV!69QIAVVrdwYO!!uRoH>MWE_ifB;Lz1fbsmM6*4&-?0TDN4uRn7m3h9TC++nqCUZ7(>4{0cCErQ#gi z)i54HwnLuai&2Dm`xTs!ePTol#nRzuX1j>ZflV+E^E^B7G8~ApUdv*(eOzNMw`I{v zD~a1|0)AKOX5V%TySs$UW%%dxbWH(u0z=eKO_R(1EbaJnMV- zK32K!n6v6;AXJ28)cjXm5bO-~1U#C{D=!k9(#9v5>apu$74E3@+!B`2%;Na0^UiK! zbYY51nMx{{PNyJKdG5lPE@SOprUbH^zduR3gNZ;7{*?|$vWBiY*Zip z=g}S^V&l4k4xgTnx3LbO6U-o%55&MQE!C%?vd0@>fKeKRZI(6+m1sh5}m-xzh;Ew zHReWNd@w8+lygONuq!~*-b5Khvxmv~pActUZ^yg2nDXujt*eNvQHRkfg{v*k2;cMp zH?>W61l^+6_-BPg?eWd2SR^bpa-KJQl3D66aY9smkO1soCV?0#%;*Jt0mg0j`u4$$ zs<}^;vL}97=mlO71hz}Mf#K6Lc%Y;>=7i+2$|CJ&UO%8()J8*d=xV;EQsJ#CoyKYo zw+sc0vRKeSh1WUaQNCn~e$M4q+!p_-dtEHRU*%fGYV>7F>8Ppuvp*hzBMGqH)UZ`V z?JeZAW|QP5%GvBo<851!sWw<8Q@TbEOf-x<_iG&iBC*;zDqL7K3eeIeRknR&?FQBN z@XUrnUTe9+sR25%HC$enfyoV`d16)zsT&kh+04bhmJ6bsidKV~I7Aj6=MrWt7)Ci( zk?Jpmu*B6%1PN7Soz-S@8{U>GTW$Di>qCf0Nhz9U45Xk?so~Xy_X#E*O)~v$mBgMY zg@|#BSm?6hE0LXNoRejqpd+^0iTNs7lIwkzm|=H;5z?O)VI2mNxWT+ZJgzO-Cq0%L ztyo@`CeqbgR%huVde%Y&HmP8LZnEbT2;j(9dF@Z4OCDv!m!RzoDp@LxVS;gWC~knJ zb>tourd77Hk#2R*TmI|T2@Y!U?G776%#~gnd(=BIMQmcE=&kVaDN^)der_s2a?h%_n9AS zVRv!0?x*d~W{IkdPh7XwYXKSuoLKl8egZ?bVl!tCa#$%~^vk@5SRA;Z{=c2hSe5c? z+l@KMUM|~aqZzoj(bHE9pF_t+t%>?Q8jxr&ZqIPld5A`W9XjPBd6eNY$N3bZs_6nI zoWf2bE4y{+eoqZ#xh0@9#g0TS3CncK8$P+Am<;lCQ|bl zFzPaLDiqeGT}SD4y3Aq>4?|e0J-RC=M(ryt|IjNCP6NJ7s6A(+@dZT8_&I2y%nNEZ zcxDc+Z(Lw5=hdMO3?a;Ef;lcz84X21E=KwmFHikcLk+S--F5#NFwO^=czQFF-%){! zdl-69yf5|y=>YE?A+f+t;9wuTqC4Z^M2-5U8H?~n4c<|82DqI!uEFBk5)Go~O%p0F z{rD5KK|HmoL|*G&fzN8l=(|&NgN^qz(L0~-y8CNFZbH5yWU4r1@P_NY=&m*pD%~hc z=?~LPHrYg(TSJh9+~KZ~a(sIc;WVGEA!3khV3#tId?e}6fmX#UHlgNK&qD}U62U%l zPjm-U4~ZJuGwsPyo9pg3@z77N?fSC4TRagto!FRZIB8I-o);jI(=PZDnwntt%m^r# zd;PPNdyx`;N+BU19>wX9IN4G9Cr?LS{wZ_`^*StF&-U`j7=kp1*&7=|oFHb=+!0V> zUOUL=N=L{21sTKQ)yiz(IomUW-*>4`?t^EC<|8}SZ~83S zxaSy0DK32`Ti;1~Vov=j6S(m+QIpA)xqt0Nmo_wvB84LxnPd$i)TKOZ^50+nm{DrD zph;`l;WMJ5*RxqTq$-slr^Km0O%Ud=nARrg6N5`I z3-`VC83OsHI>)45OOS{4F{-T(iZtb@pH(KC+`@VP)D!E{Kkxo3K_R;za`l9}O=3tn zHx7D;xU|jcR%pYFe;ej6M0J!OhCZ9{bWv~WNR(mkXat^#c*e`Pl}9nDXs}!OgTSy9 zQ=vC3TiN3!o(P9U&fG;^X7$iNT#(|<*&8IDqe0@PMjR)#L} zeUQd5A`KTRK?-@#=}Y4sKef~okT zv>;r0u*TdSs0CrpaS7yDk+X=Dnh(ZCC{{E9Jyus}5p4(ebv7(x3E=u!1lDM@?b_iv z@TXQxjo#VOCSjE*Y9x>Pg)}7N`DJoJ6WQUKdF^W0v1HG=J5nyfP}662UhZBN#V?*a zV)gkW#vuI!wI_J}!mXvXa$W`Olx}`qm)!0PV-nv$efg#800iiNCm}fCG83t1Qb3x$ zwoM($B<$Wi?o_lX1|N!gmxmC9gDL+N+kc9`C${Z~f078^OqJE=&TN_^TcIv+jaz!OoFmySjziK^&~7=x;pQeuh=K<$pW9BBrHa9JZl>H9+P-I+rV(lL+CO6VwMO) zJU28<01r36#;ERQ3MRERXL7WJ)<_nN-$qxe!BwFwoH=}YLBQrgk*79I)**CNZvwg$ zhtelnNef@krk-KTMcV&(JK5BMq^x8JEqy&{aV}6J*h3V3z(jC*=7#V0el$rZ=CHQ5 zj)9ERqU-I7ru9*7Bv53y;~MoB>SswfMuRN(DhROy(y;|HBgKO8PQY2g^xQ?HG!A#q z)9;4rAnVOc_IIClm(i3B@qL`70@K`D3$u{a>UNmoKu1^QBf>X#U4cXG3NrR_BA`qA zcu%?Z;Ld++`_nT87r;`*@GOHl251eu6kfSg4ix&v-2dY}fBBl+4b%wL+X^SE#%;#V zDS<4=gg3?3rs^K)RQ%^OZC#~~V@;)f16(^a9HH`5VE>A@DQ%=4jK_8{t->vSSkEVK zqggLo0_PE2V(X&N5T)}Kr(PLpsn1H^&J0smpYO8xoR{T>V5(dLJ#Xf^?I6d=+0oN% z_(Q2s+-?ZeizYfX$8dn=Ogv#ZT^(l}(5aX1-c@|` z{4eJ0D)=nCu1G7nFoKcMWCZ8^s(-`u9xT2A#bt%88MHL>U@>?-{}c`f^@nY%=QDB< zMcF2)3G7&OD`p8zB@9M#3n{Kba@;@M@4V$7ElxtzJ4?P|-GL~XRjIHyZW_ zh8UbHtQNM$*NjwY>qXDQ|HL}d3qhy24P^TVh?9~gIj)>{2l<^J1;$Hki)daS{8bKC zfAwD@+{xMjT>SU1tSN?9xof|^36~6TX2~_tK%uA6)2-dxRh;K>n%$Q+JLZERd~Xhv2ru1j&*uyTo0l2)D*2~Q?^_t z!Mkv@7XBqxmxs&y`{X$wlUVeu+HIdTNs4qpBkF*4C}ja=JKfH`BeYKbv0kKn*;Y-1 zIo{DP8kCyUg16X32zSS2MV!0+SU{9`D}bC0ec#PtRgi57E1TnJFtCJ)G`i6;p6Z@f zWmNGNl0Ac1OKoxqOJstf4sZh=8`g!2B~WBS%ElUX*R&vL4-_wZPpQBraI zYHll$*nxRxcWt@ZQ_uU^{LaKrOxMrja~dCy$D=8bB2_s#=`Uq}|DX>u(M@ld=WaPM zUJ-ZUl6yTj$hdjZO#Hs`@PW9xk$hFba6hHIDmrkoQCO6_KOROaTe@0O67<-{^)l`} zj0h(YYDS03vfJVmNs{y@sFeH8SmX*d7fFW7WMQ%7mJRAIib}{tv$91@u;%O6yanCC z;2YRw!L;KHR7T+hK#3G_AX0~3{BI;_xKX4oz->`Y@yS*AiYV87{Zf#CPplV%GLE}% z@#t8w7y}rtrZzz2aPZ6DvG>M$Kjy|3W^)LfxmZyifE6s~L)h8nga!KFs;L>h#aW-} zYsROsXN~CxLPO6GYXR-MyCtRl-#-q;9&U_<4TBdR*9d{>$vEI@7K&gZ&Z>qc939wV zfUw&g@pv$r+r-u=_yH>F&eVF14rnE5v zw$M}FOo7HGDYAcw7}n{rka|nsRtJkELkic^k_%mlx|XEbp`(h72s1JwLW?O_X`&>E z*Bj*;_uj!bARwWTsu`ynW~iTYp$bu;Un0v zZ&jx*ze1KpUuHg{ndo>5q<{yCfRqFXE+<&s@Rpz}0t_O&m`R`abaj6Ozpe&LGQhp= zuOTvPx!LO0jBx}THL8^3vXD_U9wLF>2rj_VFtcZsNWMc~?<(!h;L`)x0C=2#D9`k* z6&vzgFlA*ZQseOFsqxviRWQlX0s~63k2CIEN?lXRHmVksR558fiIp1-dadN7#I#!_ zx}DAXS8m2C^M`g5(CW??VSv3$6myBytN;d{?h_&^fE9VV)qz(CC!7dEG+F>b|G>DV zkrlG+0a4w()Oghl2E&JA$_-Jp#(l&^at;tsP|;*keHl^LJ9%Th`0yzgnisfA965r+AG%Dk9(5gG^N#8k&v;UJ5UQ0K?@QL4 zADU zem56v7WcfISPrd?k$chYdV7tlGWmV|>VFnKt@dQkr%KOvYpQX$fwfe{3b&Af#!qeWXTwZf;+wWMcZyW#_F1iEo z16*7P=EzRb--ThurMloRpiJw5Ssc`;FZ?YS`4pprazyL_Xbrat7T&fd2yTMp^dt#k zF1mAX6?+!SMnEK2ban{0db(3g4Wq2KQQaROTC0-fDm4O-#dqwjDF)#T^9|;PG8OFC zR<3aMCM>C~{j6-40~WyO(wEG)3aj7LTDPt5g>pvHi%oYqzJh^%kt<2#$kI48qg|J&lGk~b<~}~p6Z8j zUk^1LaE+HSJ9|!`b8Dxkv=#2O4fXZJs0XA1SQ{ZSPo^#`2ARW;z5W2Z0UIV|KT3b& z7B?+>zJa>DCSLZ@zdW=dBY}GjP>qcOf)ZURc{$+Fwu)P=@voxutg;6&RC(eM*8Ffb zsDv>$GJtaXh+F8&9oa*k8c{H(Sww|uR`kNNqdD$WqaO3d0TU2s#fTp3U zOSUOsu`2Y8O}OumiC{mfOS&Wf5_czwoZ&BKCjvan3?nAA9Jek82aH9-pI_ZSx1n4~ zFmq?9EVw9Gurre8vsz zwh+eIf%-?#hPRFs8H9XIYp=5Ml|#z1+OWEwJ8TwhnKhw~v9m&7?+OoK9sYT5(Dx(u zyP7P+KH?p~eM;-0T&3LTkU4#pfN}9_ToBFL(!9Q7v=Y)zfSEveycPSsP4vIRdx}nE*g{lmTTm;xIBbW5Ij~VR zw68;2EUIMmUym~jVTi9k~oxQ%~ z5>>9H(UW6}vRYnHjcyY76fGc*W?qwH$Fus@4wY^j!}msKB2^Jbl0;AEMAm2Dum?7$ z*`-c4S;wxeq;R$>X9{j*R%Y83_k6)Fl$fIM$3V?hMit`B6 zSq1mqh|idYN}pnZx|{&;QW9tf4N*NcG79N%ozGXC2f9p>HcW354h{6-q(6$8)*VaG z*oXifvHuPtl+4PV)lV1`a0N7TR(57u7NDgrFmf1O(0NvTZhBS)IIlU9O(vDK9@E4P zV;cEF8b!RS`x~$lwk~#ES~M?Sz<`U8_y$YN9jrPlEq@dCZG{C(bj-o|MWKWw2HR9OtEJKR;*9z^5{fr;~TdQYQ&<%$UFwv&IF=^CY-vx0oj~^EBa9#lcck zNyWs$Cyu(Q*G`4lr@L&iccRuv9-NDK|Mlcc6m=AfxzO41C7`-RMKd#V{(4t6@a*4_ zc502Ily!>TLl?|fxyn1heGf#N>9F&)w-A{8Tc$Id+`ElFr{`SUdPQ8iKX5QFBW3uG zh4EB4t7T*^`w)x)_oNz$y+*)8*z5oY>-OYR?~Flw96>D`E9TCLk>Kvv@SSPImaS<1 z^MA>57BKYazhrsw|Bxlq{~$|FFj(ih5(DVVje!AP@?r897aCG2(S+@n-CIf51ya~W zA^RhzS&mojaiTW|w`VVJ8b@Wc(Mq6Xerd#3(AXuj@J7b_nxKucXLp4?!1#qTC%{ovB^-p1w81P`z} zz+n-peX;AwuYf#tYh$NxU7ujRF>$7_FW)`d9?-#jkNSoUn!4O3KWMUHsDN6IFl=gk z)aGvd*hk(|+>6S0y+t6g$XX54a$)sVSXjUW7fT97w|R97SiQVKQQR9kbK#9t%pRC{ z(gnAq#c0b#A#ZTYMAph;K3G=~!|yM4_1!WDC=R=V%YZp*TM%f*1K?aOVwGL3?de?* zGv216cXCUS&%9YvD2Y)tiB{+p^(q+j4f`X+#ftig#!JMwir(zGxC2FER`|f>kRV6W zq4tQpB`^oXGLgopNhGVpqR(VM)Oy@N#}R-BpND(s^Mo}-s>Wz^quhXs1UFA?to*I% zg80ptLihA0yzz1Oz`;?F?Qe{!^z3@3g)O?`u)_`FryzM#1g>(Ikl);Z#_KdZEsePb zLUU!?L+S_4yI0KL~p_np6=xXELL$jahoi`L0lhQ zYFsdh_CJw}gNS(WA;demRX-wmFR&Q^IxQ$CqsVJcC&pU+~XP_v=j#Ezz5= z-xpOk>aSa#H}D?|Q_M~6D-AwEEi$o&i`LCeMZgWOl~oZ|IKCMOFXXssnZq~r9%#!;`jgbfSL?z&UY8Qd_IHaDI9U1TW2 z-iC4gN>9BB@3yE)Gv^iI^)WEHlt#_wKunb%Lx#b+jD(ywZ5P2HlL< zqLWpJBkR@nDoAo>_lOs(!dTjZwe_ahX{Tl2l4#iIlkHDd?&20^3XLP)oID}JYIGP4%u|2OvAffyyL>lgVQ z2Khh#x!L}Y7MoRU>^4~uzH9UZi1En@StTf+{rS)=GA zme=S;saNBAFk$uL9^~SW8Nwc^)oI4#uoAea7&1EaO_Eba(Mn{XHCMtGQ6QQWM-%;T zt0cFaZQAZ_Pn9e~-A|!@5$jZBv zKg!7CtlBI3hs5V7JpusTvzq00y^u^)n#=6C< zd%5_}uiqAT@t&p|;J#JzG>0vHXKRns;wC7{ZoQ1IhoNu4Bx+8wT@@uW%&sq17(>cV zd~ywm#wn;zR>{|DJHa%7qT2-h5{s5PizX!PpoD49i@q|t|0ID+hQDz*b_F2wEN};GwAr^+Gl=p`t3R`M>a*ieP<)IQ%-Nwp~Xt)ut&3iBdx*v zo=5Rrx57SK<>oA4I#|M)C*>ImJ+~IYS7M`^BG^+_)(y6WZANPGNJcMc`qHNaR19AM z!leZ6=hk~Ux83Dc{lr~f%7fXXMIZjT;R`o{2XDm3+9UYwUp%TzTA)r_MWUh)hi_&P zyB6gsDSU$_j{!@efh#|w^&ge2FriTkRh1>VZ^9a@u9~h*Unbt0zwbI^ZwyC#|0FwS z%>B%Cc*kvR%^#e5C^r+n@~^NK>qM+>6f~#R?&d1EG#69{x(8wOxr`C?{y)Oru|3l$ zK-LX9>9}K~W81dVv2EM7ZQHhOy|HcE#>woN*?Z6VaL#{t?yIWST2IxbUk&&Hluq21 z(j3ai_JKC)JFnQkl5e34Z3?JQ=>I81<%WFCMnCXJLi~Tj&;EbGulV2a+lldr3Ft4u zg@e$~09Wlh@i#Q{MOq7DLNq$ohshT5$w%K`yA&D%>euy#=)7yq-mcn?+gco2zwPgj zPoxh@siLi+6{QnFCXu%mxpekV!lMctQcvH8LKoNi7G3Y$h47YZRS7@-!@S!D$SXy&W@h>3rQ54emY8Z~uZH&C@@d`k>Go+p#O#-&`mbioz>SXXXWo3W zapIO{u=Xq8cwkG8D3H)Slzxp+(75wfFREIiPXWjjqcCALZGlh*VuBot9AFwefzpx2 z!@U?Qb0+cer|dq`%Wu-Z#zo^2`1I?!b-xY_WJ;y>Ulytv$LzI&&SFoOr!4M{e9isf z98%b;iiFW1O6cCec+7&hxg`wvM4AvpDtyVPWdFs$eCEYA75R?f{+BO8EgVp7URr|9 zB$q#*E-Tg?Co;+RZ4t^D&sQ$^Z)c=l51RtJM)WU)>FKnT*yTX}Qs_-{f@|f12G=$B z!_F}edpsvHTY>05ygU`&a13cP55sx*@P7!E$?lT4RMaERi4U^Nd0YR*o_}%6KPIW_ zRW=@e_7Glps4D&67Tx^YL3)UtgV7N`+5}>2QTwk)^5AhX z)|75@WuVw*Vv;_deIT3HI^&rs_5s`B~@ER-Tjk4t`TK6qPWSnR0 z9<0MAd6P!kldJxH<`(Lyg}Z{y2(BeB`?qX~{Rh19!bSN`F+@EFw;n*Q|AP2{VYyuD zcL&+jImZ)WYYP^M_A<}9_it{?fVa!u7yK7q_rgovC2chosr!fsHZQKLOSrqngpe?^ zReAF(G4%lz*X^=%g*p6LL|yPQ9DAuLXTNEh-NHJ@m&iub9^$8Q$ z;HG_chJ4Pk{=(i0!%iC7wffJ1-Cu(;z08(bE9VHwsMUpDr_PxgIb0K*Flz`C!Q6js z+T;(?Lgv(zXJ|XhDyN9q{=3<)2MhR>depnvx22z? zEl1PV1Z>J+<`<9qpO|6=T7A>)a$ z5Ukg<5g;UgaL8cx_Btr%3uyyjlQ*6Cw;%`i^omy1DoCOH87*!8$vN;rVCLWAx!(vz zMqxKh0sm-Slbh~UbRF&a!ZuK_`#+v4Cr`5RgVws8=U={x4Gicsd;e8GYo~h!-eKTf^(S%SsG4b%Z*W=xHuCm|&k-QUX1)yzL zM%kDPv*z1pIcjj>;9GRysW#_Zg--kK^P|5swqW24K4`u=fX2y5LNai=UWvurrPWP^ zQiSz*N6lM{pW^23!Zm=TJcd^Xi$eTtREo%#mK2b;{Yq!X+%VotcMf(qN@1b-WG5i3 zV|04)*q7V3JHLV`_9!j72=eU3`O7iH0)%9ShK`V7T1OXR9e+soBA=)^XK(HGju?sD-|A>Dmd2{XU6v?$UZ6 zYu8WL(Hc74KecR$Jx4T}b16x)wAu&*QSM(+jlitmj0 z8Bpw5{p?=-?lWh3XElCS9y{6opXkCTUz582pj-d{gD%VeEwCCt?i$&9x#n;DZ;8;! zRT0iga0Xqc4I0xWl+al#F;KtcXiEYn^4P?^k9QpTc%#+JTK!a=eE4I}6o(F?|Ap>$ z7))Z&trgsOn1F9uf{sMCl^&Nt!bv=p#k5-XNI|<*gakp7w#FS!DG=2&)(`dZ4Xm~cfJ$YjwzA%3^R|j$x zc0Ydqr;`Rs|6BnX0$nl01%n@Q;j4zM5+t+2bs(Gh0k_D^k{^{2QT2%I;CW8P7ZAet z$VPxATZRmGWLVWCROzCrE`}NPWm17Cm01g_-+P8s(-BiB!!K=IBms?&U)862@W4>A zG6Bi`1MZyEwE!Xge}MaWI;sQ*$B@FIXx;~A1Bvl1#bXZErC;gF2d)HNpu~rqtotql z^7)5cG9s|#f7|GjAI)7{f{l+W8#`X{rZLK-k4vyfjKNXNup=QSZ^XwyC=WAaPC;2= z6Axy9JN)@AFSY($>P?-BYh^DQj)BiRLyPS99p@^eVSIo43(=k4oh#~jn%X*(F%Ih# z4|Ge^=>zO1)B|sN$D{h#-WiljCplAEw;qyvu)X*fP`rgO&gvcJhi-7uEvg1}vS}Kj z@4EfrpP@)5R>4yf=QTE7I31KnoT##r8tp$n%q ziuq?CWe8XUVf>g>1p&qeL3$GLBc*F77OHki_u3aY_eH?_=ie9ngCEWP)Lr(jrO7c- zQDJi9%DP0o^NbG(GF#9$yP8oSVzu3FKbM>%*hSCRsDzdJ#1-$67>c9Kf#UBj`AcD1p+b)1O_4i0s^u!aRj>uvcDgCia4G+8F$%wGs9+Q z{BFP-27m!^rd?BKbET2q_G82xS6C*!?AIYtCELZbYa}ziMq*BH)#S+=5FOp1}RUVJCSVO#$t@geY84tz;>m4*rU(@ ze;*R$Pr+D7U`UFd^*qV9+{TR13)5a2r|m9@Q`kBDL=y3FyVSMdtc$-8*S8F6?s~v< zrF@D&u2R1~#(Kb%C$r>y9(E1sw!ZCZzvpjGCZ~{oW8AAi+@mz0 zHvmisM`1IfQ7=XlM+pa%ZhQ~>pE}V^H|Aet<=tGMf9e2H@~vAAdp-35A!JB^ zdK}-h#9>Dv(`um?u`G5_*C%Seq@kG`t#S(P0_YY2a~1Zp@1&vueSNF0-&0DNE>~Vm>@3)r z7}b$y1G3X6?l;KDVjqR4c-R<^;zA$XOjOzFo=uS{$Bkz)im7{>_g={3j>~F(?TusF z#eUkdo0jb~XrBVquV9p~CC!&0^%pS8k2y{BlUQip>z={>ZB-~6iy+8*Xop>^rW+Q@ zj}6tAnC(%c@0D-xh;)qSF8?Qw{6V^{O)rAd=Z7!7v zo{{`*X8`ea4p-W%!-$5FEZu#{>I*pka8BPZpoaW;CzkI>ks))8w_`K#WMz0Gw28{m z`1LI8efu?sIZMoZ}C7*-8L7DTfeO8>BEyU6~JmU5VC#AWvs^bT9 z*q2piaHTQQOB7e59Y$20rY-g>fu?{{IIGvcnc?!KK^O~TNEArzc&EC*R!Z=624qxj zI@f!o_7>|KHZMy6bU>=6d@1-|XE4VLe16>O)Fj?}6Z*Ww-db)sexS?rv8=GD+9y z@B3DJlM+mxoX@|t2#ug28v7^w>y4Hb(w=yR4MyXB1q6u3CkwIP~#MdYn&d zGo(y-R zZ_O_6ADjkytmLV&isvbqGvHV;0WcJB1Y26^UR$SOU6Ck2?Tjt2+Dk)k0I#bJf0rI+ zfhB@z50lg=1^fM&X8Yf?X%4NXMDtEDLPePC?7{~cbC`5dtXrdKhs#id9LkZ-xS$0f zVjxwsvrvQH)%hPj+y-PR(Cv>)R!yW1+O-x0q_pOWAzzb4Q>R1= zz+K+q*98xZ-DxcLv^jd>zdv5lU#f@$B|as+)9pMWQB$n8H<=4);w!7)^57Za(ARc= zqXV0?^!)3rpiAI)y|UXisUQ7rFpNX9V*Xe1r*chGE3C?2CbP|ll|(#}ALsoL!=TKc zF}F;0={B}F$%noU63I%D8mX}y#NpqEfleCEKQN|yHSbi>Q%A7*nSRn~gAuJ?X5reL z4`j^htQuvN17H1@JoM+A&5|b`YJSs7njPUt3Zf?z?Rgt~MV0lkK0SZ_xejra;?%8g z9n-dNM4`GtYbMaK)>_zAJ+2gUN$Xn%tV0`xtORvVs8fFy&R-tiC`xB=r-*-0y0@tG zNl}%2oH)b|B1Hc|Ck^AhXQ;t*pFMm*lqfo;fj_M3pXfRv{bG2~rRvBysdMf)=A_lu zE=7?oTD?TfU^$cNLPq^X*OnWyq^{OEF$eQ?Qs5;*z#a_QlyH6GvVm=JHA&qB0aMIA zgn47E^)}0vdYXLS4+<%WcH64!+g6Ac95oJars`&O+KE7pd2mJ<6_J)>-1JfkZ_c9W z$waY{xin~UosnoR0YbT%9Gn)DoQ?O-=6oHUcC{CG-@i&#@#GLFeFzY_uhgt*RMrO_ z^$wHI!m?vHI|S>b`c?E=M3DyI?T`Tf2Q zNyeL+MdX50X^y}N-R_##oI=%=goj$V^K{`iz~jghj%xz}5|M%rlUz6|!OuJRIV=Eq zgF_cDpnf188j_vk6o=McS?*>TrP9g7>zEowe}g*B-~Nvc!VYQ#f`4Z!sQI{_w6`+I zn19i;R6mZ8*G5&}CHa;bA3rjKJR1}NMhorBFv`6NEok?M9Zq1L0}g3UDff+*z_;}% z57PJpMR&0mzCh@?eQCgaNbV^82R5PTTsLF8dQqgUI(!kL_6~_P{M;TaGOV;Q#8@1D zmFn;MHXB*k7uZ7e1d#`I*R+*_+X7c7w?)v#7n^Mi-@o=B8bZ9ax`;xNU(43Zp4}dDvh4kjL&2J&jgmdGY9NcYyNa%u8h)j&J;@aI1ZQ! z{L-i<+Mxo>Bh(bq8#lh!V?~%A@VxM7Syh`7ezJmXhy7 zYP!8y-9JBuhhV@l;IDKWl&rtYqlOk5_yq?&kBxXuMGMH3F$n!r9nNIi zf`6Ip0Uj{I_E{UcNvYlp=Wv)v zjIPU@qp^$mDP|&g18K>QNoasUB~AfBELV)QxN;=UqozM2&ylX_Of;KfgW0!GyywID zO7P#IMNVO4l@UKiiqb&8w`&8truDh883Y)Zo3>*NjrBvfSa*M0e{2G%v6VmBMuzRm zjKgYu!tC-3ErTk8jq}dOwW?JW&(5-L>r3wBx6AYr`e?q3cvZwC(sES9;DStESk-7G zkZ6l|j+C4_j@Qu;@t#L_k1b^dSu(?TMKwS<`!b9vGk-3C;r0Z6fAj-%jk&xZQ|9;g z-Uuh~Ga-DEZnE8Dl*2}w3H-cM*+c60wWUv=YkvlhH{oBB=MU$*?NZYzi=Cy} zXro?)Vm(TNBu;ETggKl3Q&9I!ji-Kt64*l((V`^lI@SI&R57x)8 zwfs-gh|C0!FH(5Pp3Ia@=cFPuI6->#A>WCBoYV6>ebTtdynUqjjgY(Ne=)8?H`y>d^lpND;U1kbPsD?#%zJknR|N^ ztXg3y`ig*|Y*kD(gzOO*ydXDI%}u%?x=>2K;p`eHqNunY{nbFp3o~6TUOTY{mxj!L zcsN1WSV7NDts&m*Y*ExJ_3%))z7m+~d}%P}8CwIN&Ro-2>l!%BoL4BkzIBb1@pRZ^ z;I(gET)cQAsIi=wtUReuo5x!xcV5h~FtFYXj(=*d-BFwubEqX)Dz?m6$bpFi_Lg&f_?SD+wYx2PMT<#EzB&_;q12~B~E@Xp}>^GN5cabYw` zw;hJs@%OgbMtEBHaWiWpYr5r;BU8!>hluyWl}^ya23BdT#SVFUbQxwYI_Gxpl41fR zavolL4eam?;dicr^&a_umFu?BuAY?kdkwx9^o?Z6&VDo!C#005<6BS zeDYSLJz@f;eU7j^Jk#wgq0p?u>Pm#de8CA|`b&xt8vC}IBPt&A*L#rNAZ8H2gW~VS zaFh6#t&+1T)Vm>X_U*W&d;ym+jp0A|&x7dnNK}+5_l@mwY}p1Z*{_butl0+hZ7jEg zMSQ_O`{=-RcHDJ5gs6$0dvV4U#I42!DqO_dg*gdVtVhi{wDQjM6rz5}$XuP)1Ch4T zRTP3rCX7zkMds295_^#a)+^VgN}KCv@z~GFYqF^I%p|oXL&JJ>275jJ-!((tcg6M7 zF)~ZEo~T7;3$i<0j15Xf{j~aBsbmXGhB*;4Hp45*qzX2fmB z)_xOJB;~){&hFW}sdv&)CVtu;vs%^31>pz0L`SObUUIB^`T_>H z^OIn$^k)nT{l141B|dNpd)B zgv@qDk#CI+A^nH_(g>)eIYvnt3Iw|hl`;{&xs;QLtI zGyFJP(hh{e&TvD3qSE_t$jgreEkGOyjz>Zxw&*`KTs9BY8PC9aGyAvUeMrDhU-5Cj z)UhN;fNA0ff94?9c-n701upfa%UknttsGYU$&<5Ije*lSdWnqcgB*+zhx)s@c8VnS zYEuIR6(ZfM7o5~94t9O+kRX2@I>A#{D_m-dDsnAm(%L188q5iH7zuYh)~Y7-d2mTT zkbb(Rq`EaLYCxsP?~+JCy(8M8%A_c}7V@y(a=(_MxtCwA68ftqzZGvhFAyy!{OMA# zPgM1z)#U!63w60}p$jXxUV= z`r8bdB(iByxMV4g9-%zxZK0vE(pz}|qn+^O+=%7rD!k!VG;M9qcytIO5l29#28aq; zdzC9}$DK`4EUrrn8HRV#`64XMqKtiqTrdikb^uae!@t%Kn-63EErh}~JdG83=| zrdr@z(``7yL(;FE@-}y~OH{t}QAa7Oq?xy87HQIFX-Sl3MmJkb=%^!D6<}0Ws6RFT z6j#wcGg9-PB1(B@-6-ZMY)lku!VQ4X`!)`F%V9UIh5jZE8mWf=6(AtxgSEUgLFQvo&tNVywo|X3EFtQ@AUyY>==l;WWD9ySduDHr2 z!x}?vOwm)JSeP$+eqR7Znw!x?Iy}=VUrHPak>Y%cQ9Q2 zYhd8E!ew9p^c+lgJYj#D^(bS>(R;QMeNlJR)CCwRTO2$`>A|xlyo6%fm7vd9iqp%5 z2^V%sBsgPCN=A;~qZ*}F_^J1|eZ7es0vh!S@f&mzU9sWYxV|=rzN8ik_4jEaXB)#@Mv`5)O=W( z2eMpNt+39Ru{JEKve{r<-jN)K_&sHTF}L z!k35(%|&=ehWKp%`GgF|dV4nsd-Gx*wH*zk$D4+y&{m7XB}@<(@$55N@O6;>)KpKI z;^gWp0Gd$m^WzJFRqS+a%6<+`<@y=%q|osJVKHJ|A$`{2h{jArG>piJY!DQ~LJY0m zPLzF{g$a=aP$%lb9{%u{WlS{Zw}yO5@XM%*BQHxXt?jUHSQmOBf!OvRM|$OKI7cpZ zo&9wiw_m=LHA}@ZEOu*e_M|H9d5GP42gWm&U&aLM*Y<8}fonPb@`$ zKGL0u3N7*`$quJ0L1#rDB#-0!bMvBtWTO;6wdeJ!*cmqPi4w9R(bKa+35OLbpujp( zbzh2oYi}JU%=5UZ07Yg%_0}B|Pu0#b6-L;orUr^CkfEdkTAqb|ov`p|jF{&O8gDW%*Nri{Tw^=BMaE zA%LxRP~K^$r5Ai^9=621IlW1(YeB0cUcZ5I)A zam9>ezXo)a9So(tRtDS-gOc`ypp?&sE66j8deIG(O7yQG5j{7< zWlN%F1tiaXLYm7h)q&td(BXwZ2FUXm7li<}W;Ar5F(2BP8F$74d-xIk_?C0uN8?*W z)u27MJhQmB(g!DjFFaaQuk@EQJy`X!l=(l0fa@mxxm&k0Ive$DW8g=e#h+a_9dD@3 zy;{vRJ9M2bp8i|k?T0bk-4b;h&qOyv4zz^?i_=?xg+Y6Bvu2!P+nd!tGLW{RG&Iq- zHvGY7wgoM^AhmO3=swWNZfdvMEvtLubtY+6PXX> zsx-Ip+3d?DK7&~4UE+}lT2w~7#A4TZQDw%=UBRqjJ-+dbq|qx`yj+LoJ>wqz@s-Uk zIkj-c&53*b2xW{^qFlGoo0rSjpQ^aMU zsF{8^p5^r$Em1kuB5W-r>IY;f@-iJ&O!e(JzTEHVez?1`q2s~GbZF=}1&fkii(E`n zq91R5RpQs!w)ftr`L<_H=bos5hS2#YRJ~Psz%5k^t6IWSG=j(}w4qP;mZs$hlYYz;)6?-Et4Vqt;MBUK)z6EuO3#T){~F<$e8ejr0of4E4n6P zq+MNIbN@VhjX0*&vG#edDx+gYOmbuX&f(~cbbrv_D*`@3GupR_G`efh->ZUDldEbT zo;9JO(r0sfY^?qcgz}8~YUpHy%n-NRtB2Ddl?li30JVpp+d?_q-+pQJFw@PI`*-QB z#!Q?rHvM=L4Z?yp+*-H%3*fvNRWtR}hl~*wvmJHk*VKjo;HQ|2?uuoZfcQJcaUl0& z<#WM8R;#UM;b2VTQ&*}<)58`vNz;eUdDgWr z?#B7AtY6}@F;%kmE+MEeb5LN`kMQ9Q8lAX~=X9w=+V&m9MHE_i><$Vsh&gNS&NoO~ z4&{0;cxWK{e|O~w_!4n66SmozFSdE?nS>asRG95KzCJSJ9g+Ak1Uke!S_4}&6I+Tp z;Y*!!?b|DwHHFif8vTJ+denmZm_~`LVj1%gKrZ%xd!}&4T+N=K7JqM{+iDPE4h?kO zyPEyo>Vw9?DDuwflB(H5p&FIHPImg;qp%!yEKPnb;ull%LD>EAo2|KpBcl(Iwgav> zIq?qstWCON^inHWP6;Jz924uO$?S=Fog=ex-X?iKsxk?e?uvOij3KK=C@YGS=|dVp$EMY|#}( zQnivMapPGCIs)x!=h$oRH6_pDKuz z2~T1#<;c&>@1cLLV!JsEJ05g@trJR~t8tBE&g8}}xR09-`d>VJjDMPRke z(_^biDLV||8+J*7=)*aj8q}yQpR3el905!!vIg?{NlI!9_%;2TFD;G(PG42+0Yq-5 z+#Hj&!MkG`v%l6jwkC%7yIpZK=hA_aaNr7V$4+niIzDGHf^A2UwyqhJKXORW3i0Rt zHb2$Nb5kcYa0DBwb<#yfRp2BPTt)xodThd&2HS87htQuDYTy7r@S3CNNE4OW`sZS!R+PK6d*g%hbULv09Zb9dll?)S<3wU~)BOC+8~ zqke=v0jm0NZs)+IWk3P9=2n9|BtcJAtCDL?9i@Cz+;1w}Yb0k|fsdRr+8ot1o`sTM zw!*BaF3dBUtz5zkUjXsvukRWX=jEOoRSK)rl6r)qcIhgTm|x7&AXEZH0`qV8Booy# zFjD`g6Oh*y?Keo-F4wO+UTyF1Nf+90!3^?RQZm6vX6>d(?5N`*jFYga?ay z3yJwz8YPi5lQ9a8=>GL(JNq5lffv*+GE%#A>IC%4rNq~%W~=>OhARanhIIKg`O3TSgRu6nu(a-+4QC~R zG&SWm3iW55DwW?VaHUVBGtyb>#IlUOg}k0jRK&-fdl~-C7|ncWyV5#wu|V7f?4r}| z8cj91tPd0*xN0#)7>&R~)NjVR)GA6L>Kc;a;G*rK(QRdhT~FvsS@hL+%e@2+y33iQ zTaY-*%Kw{+d*PbXZ|;x%=PVyzEb=($9%40C%fBXktQS}6w`S&qSwDTw4(q|tI7Un8 zWzED!lzN7*FG}-s>!g5WYAx1|@H$re11Mk9C6(uQhBbU~>h@s>O$fvGEek|33{IY@`8xVy^@3hG zKX^N$o6fOi4dZZ3e-<#2hmG=>F6cY^#-fXffGwT&In*Nnn_$z5lz}qzgA5~ zViK$?mZ4Y-IzbkvUppVvW2|gAHr2Bk6yXoZmkvdh7SPb)ckU{`klcPT} zbY2aQylEs3aRia>Rc%&Qu68o4;_AB;E>a97KCqPkRbLj0p<~EX$2dWm<%gyI<$lS00l~LH1zqEpY>zMLTIuj-0iX zsg(2?$s<*tn0ha7$@40gte8C^EHC-$z>r+T&3jhE!JeTRWr#%FU5%jk^(e30y0ieP zH6^v=Oku+mRl^&!JXQKU_5I1)shC!tjHJ^bUL!ryvUDwzwcB~)BF8~Hvfl+r{^SpC z)2M^EHzfW-ToU3jSHCLJF9O)@@xfH4uRdZj{ZpilqgOQppxi^F&@NqX-s3n9Dpu99 z1CVzK>sYCqh2M3PHjOAF^DXf)KN9Mc$=Wmx)E6MCGXqR3`Iy-tXM02}UH#{_I1Ih% z6)ypaftY0^)3dTK;oOI-&>hkBNd=L5u6f80_zb6Uq-&s?ANS^t;vRKmYrPEfbm-$)SZ9{$r@*MDVaX;k`%~w$ znvz&D&pL{P%7dxR9ouX44k_OQ*rT%>?4Ww~Z*|>-tQ`T1+}U!*0;_{WjD6zZY;rM1 z8|JZVNy(!d0-Uby?ewn&3nUbSQZ5}T**r;@5JHKU-|l94Q;~aVJ^Qj^C?D*-P9P+0 zhM~9NIM>dk*B~%zk~>~3#-NKNdWI(QVe~ps_q=pP*yW^hjCePwtbaqwA9imTV6llS zbtkj@lX1L(*AvX-@s+YMbfXR$?_n_X3~6J=BS~fmd|OVcNMxfvY?DB!#f8GGsXr8w z*prm4P^MXykRcmF8pP^#wHPeI>Q=YZ3%t>k#Li#Qk?_E^#g-+MS!RpG(_RTSs;RG4 zk&tf@_h;vR<6}4weO`bo7Nm>12)ttm&odbe*doI%OJqTo7THU4GhxHrcLojk(OXL{ zRjV?WkqY;S!@;Fb<+H1sN+58Hm`D9mcvYrMDWNiEI&`Ag!sq!u#!~|26YOi`NE$jV zE9~C=_c}gRpNyLIPu;DhR|SH~`fte>vB>1Cmxrd7WJGJd1wSxDwh6uPkjBKLq9zH! zJOv4?f~{BEf}6YCYzH`xYApIHNn?ZW1Px?yMTEQm!{Vcnbe)SD!hk$h%U$AqVm0d7Dh!=+&hZ}|zWwe7-f z85=JCdxY~R*_2J7hc9p{$)aSY^dbQh43EP9>=eYY}#L+$Vod{fE24|_oVA45Pwyt|9 zPM7RGwN2^60~`O6A8j&;KosNgvTgZ1JKHj0V(YHlRUy5mGEo(O7*0{8&a>x5sw+vu zJ|jyOqo)icEhRmx1?$^HT-ntgpE1d;Jmgv>>7rXd!4(1OJ$g9>cR+dCp(mxL;4vNV z$(T-`U6IT_jMhCdB*0-_+iz;G@@+vUHI0~BSDpc+>mNO+qOQBoays2fOa$_K|G=@vwKqwr|DcJj1a`bC3I` zkkYI3PXgkAO6#SAS8nAE9r$cA-3}Ef7!L|0xhlH7PDSlR;~=!IwQGd5Zy&5S2(Sa6 z$yN8Z%{*Lu18261BvJI~);a%K@|fCSD6{szQWwXTV9My9L)}H-CFhInY4I(Z4t#JT5TE=%|Hh+ z$U#6o6l*6KJ_uNXk_d}abf2%HKfY5iX68Q=jCspbI*wb!PGUHR{SG{ElgG)jCuoSj zyO^eX@bEC3tqE`k&_cx5d?;#jkLK*G&|(Pjz&Inc9S&ayo*h=L4sgc|@A@V2Aiqk^ zH-BZ$IUoacvzVlXNrld?*{zpe zZvVGyVF4~u$oa(n`o>mjEO~O_xCQeWDP$@}Y=G#8(_Nl-VZWES-2B7W>k_57_R)6Xm?{%epAMd;eV zg(4yw7mzQ@;8eKBEmI1r!C$AzUe%Xw>`^KtK-;$D{7RjLD7wm><2HdTL9kAU(l&RK zdfXP_6$Qbj89jOjB13>fwBY*L=B%*a$xn})h_U>;f7*l&!i3($p{n_s)t@=w2dpM=VJU(pO zwOmrQA{^%KV=Y9hlTJ(n4~+G~`8b;qRjex!y3%KVzl1LI1Mb74+pF$q$PePf)5OZu zCant5=jW8~h%PrE_XEjISqyHeQvd3`LCer7`sXb(IyKDtCd}^|+!DDQjynBxS=PWH zEE$1tY2T`vrcb+W=nK!TL-jH>3-G#q(`?ZR7#-#mMOkguJa~`Wx7Bg#rE#j*tKgMY zm#eFH;WK1mAuPEHX+wW1K(ee7k2J9Qjx~GKNg5fo8kxpauCo;LJQGm z@`hT>ZP+2!W{D%Vm}xfyCA2F795dLuC5C(Mq*kYti+hse*9*w8-HU>WhTBw@jpxV7 z*3Zc2G%UdmI3$^Qo9RTgCwO+pd>W`2d&m`U`}gTaUHPNxFRXUs28V>hvo$&0sy+B~ zgW!+Qjm*m@V0OifP#h=z7zVuYNc{-#*}?3D<-aX_hbrqcnN5=k&Qg}H-e0Uk{q><5 z=Er?yj=D%Ozr}V-J!YvH_iZ(b+N^pt>J0W0)*5kObSw<+qoe;syQq@E_B>nfafZe0 z?qFyeqj6Rw5*rcDPwk?4KM8$AUJu?7l81_Asa38lAX8%MFNk}CaX_~?a2-<&ljZ!q zi`PK?PI&Rr0)sSmjKHoX5OylYws(vA8=c8txN-j+Z!fCY0T$SLphexcIYQjzFE^5l zzuN8g8wyp{VuBHyRL3!j*woHJHru3CvoRs;JiYMx!b`LGAcTT_<>rrvPBO)wu;Qb^(@^!1V}gq+n6cn3rkV&5GoIYR+{1Of$~EP}3^X83@# zbd-!u_*xzgK~>khq`y!DkymT9URKWvD^<}1B|(^L`ky|LP?ibI(v2n<>9i>R6h*6; zC5EQ9+zSEtU7c)q*r;q;o-j@51?RwoQ5|98uluEvn_VCrV9Y%wCRi_8yXqRxxmHDR z3cmBz0l`1ZrF>mx&G{E3Du#Z=s$6?UCW(s<#k)g%sb7m- zvWPHH<}kz@{0Z|5PUYE33eB=?o5#T!jdXj=d1No0LB^6$l9zkao?-R312-oJnucn)hkrIC>FL!5iwJ{4$OV{nAX@jP;FgVhs9HhRtSf(g_`AhgE z0W-(&!!u$YkT;x^ihKQDI$PEjz>qpomi!PA@qd+&>}-{CMUZ3LjP?Mg)Y4PqHnIAPL%XkwC8J|f~5YrG10v3)6$ye>Q2uSsWi3qhvgnm=f z{%Go__lq4yNjaBAOe;&P@|k2m@cwAEW92{Bi+8Lpe^v zd8o7yMrbIS^sUs^CUgY=2+9e&q)^C~h0#Gf{~xx_DM*lL+qPxfwrv|-wr$(CZQHhO+pg-eZQp+P zeVrR8BQn1-BKO}~x%Zl5ppoS1-hnJI@cTX3xgC<~ITczDT*63a+8BvZuQ%84y98n} z!O%EFKH{~Q0aC|$H-}7z$-DmGL~!^O*o+!7jK8y^9~A))$*o|qn#A5X>=Q-=iRw`vR*}4mOlQOEH5<@ zj&s=8Ip3Dt74TibUnSY|PV2kMw4{#;IPZ3&EJ5aOSCr{HHsv{AjtVR*gn}H|J^zU_ z>zI`-Rh#K|eANo7-(tf!PsP>WRLkiOwMA_r0T~ku!yonfEZUz7Vr;GSF0O$3_}?58 z#?#Nn9mXl@>(-B}(tJ=J$Og0>RvEhtr0+Ajl2eov)nZ_jv0FF!@8~MtehZ5oGk*hXDs4 z89a?`BbE{o*e36xg2r+~=#@XxDpA>GIlpGWh!@{Q(cTG;ZV|+=gS$dF?(q@kob8Z) zpvFr#11-=!CMfa{9lTW7NOK#nV0@~L09kSwjyv|8YQbEi#`w7-mWi%;JQ0ueq4u!O zd#Ub7Jw1wlEGWk4>-$izIQIaODTSyMW+0YlrWWEmz(q7B_N0pxqxR+6iHu@o7egqz zawj9FD6c|Nq}q7ho$!&Fzeq?4XlWf}sFXToz}|3~yP#_O*hvA+-0m1A z^C2Z;los%$#S>6Op6H>CnnLD_0x%Q?Lg%_HgiejqYIEExAtx%3ehG=nOHHF)E}0+!QLjfb5Mo7cZ525)UJGo zAZ*=eq387im;@8mv9ZT2Nlsx#$cxD1ibHkp(mk~bgSQP7n#H&#fy|kBmu@o;*p5j) zH^}XfmSnbg8Y08t%5!d2^XPZ@V#iUB9s5Pi6%f``lLebvCL$o*F&%i(TqqlxE_^|c7sV3!abYE zS~kei{Q>b8VY0|F1+uq+0+wO)Xig*6jV@4b6zHJ(X~pGD+&26!9WerhT{&MD>S08D zCAp1Hh`dOYQ7g0V5wmZB-bxGxt`1^Di!6{QNW8}Z2{qULzc&mtJC8G`&OL|wcV}fi zYGFb5f4|M`gR>&dRygh%8M{i5^p)4W!1KUQKUwW-E$Vk~1AW*4$How1(bPDbHEYnL z{{X3z9VY)Bg$AO*gsFB~Hxd5u^``u^u4<9P6*Onsi1wGClkShN2+>zYUz1?nkfG$% zLLldgm=|@Cm8G082*xYw`dm~Fw0|d|lbU7;RrSaq-^j@x0RV%pJV%v&K_8U}vwwJh z{{M;7t%0o`Ny7v`3S4=k!i=cgsrxue8u2;v0C>st3aC)=7A4TtL9T-ZK2-C(jQhx28+#_` zR3oFXl=TRi6O17n3Pc|ed^P8uq=8B8NoW){Hu`fCb+}AdkckuM6)Yqt%u(cYi7InM zbi2QSJdmcnO;1TO4pJs?>OfsAEQEE_9I=iOqpUg^vC=wF53Dg9>!x^+IK>EyW{P!A zbo__Kd`7J5;E!}?bggI2IpJP0H*f9%9q>HUP5@2BI`cFCxK~=-N8Vs1cZd47`u1FW z*ndk;-;C{^4mA>h@#bbSf#>o9D`)F35$;dyqj<2Kr^K2cgjV&1f+(iVuF{vVd`Pzu zYiV_tZ%2>o+#ShVp^WnAV-987dn<^|cu(5cD_Q3}kz6uCg~Tv;tZ;bNB9q(mK6lXd zlcj?Kq09lqU9&+{T=$jC#AlNa1X|MYeX@1u)*~!j6$Be27x|o3^d#?ZKcUZ>p|0i! zf}!ma56vnZmg4AxGlr7sA}+bj7*k#fd5*Ds2=dZy|CtI!X|J+7+@`XJovU~{^8zKt z;qA-ZBhj&t1#Fafs$tU0)cB^X+^)m=<%fKfL-`u6k`{Av9dDskv?$n*<=Y7(v=^{a z72ugw#ymXZm-Rf8DWeFD-^Wi2#%`6?$tCn<1O7Jk&D8-GTD2csO)4VhG6A&W#Q6pT zP768bW6!gdzB~SIFvDIqnVFJJRD!CSDMgtMgrzuX(^mDlkJl>M)27MP3sN=>VZPRw zr`V}h7s9+MNutei_OitZ800-GbxWGx)CiT{ZUAi-Kbn*~DWKGF+*DJEOxp*t_vp;u zd89vfZpqmv*;ME*htGKi2pJ#z*arE(#O?KB;lYmb!@?aiOd17indO| z^Wnhh#f&ugQg*J{qjZ3c-D{=)gKvk14K0L0NuP`ubbSZAXaNxbv_3u@{cKO`QLIzVpdl&n;?HNv=IPi9F+_!rnwskgOA4+2~q}rV1_0Q19K? z!;i{-Jq#MCai8^WMmlqxzAPZ=LIRl&dFdZAOIqWFJ zz*|P`d105IPKe`$mOoDJlABz=Ye72h%owqv$Rstx5iq^E0jLg&WuP7a;e$Yvx)hTtYj6R`VLKPhFyG|? zAf3~>S*I?*CWyl+J~tfalbf#ay-d*Tzoz*-{5KqS8zyo3B>e34u$mo#e4FhD|3)HMj80Df^_59WhkYMJvHWV+6B)L*7t-hZcC^*oVvW zGrFi^wA@I^&Lc#u=7C$k2R1hDDa~KkhKdL)%b16PbwWbtz+{#msSY|shwFp(6%$&) z+vJD2I*XImG{Y`H!p|n3X?DJPC?T#4Rx0kzz!Yv*?O!%(QNC8o>0xtG5mRIDRrLLS zXHL(?9M2TQ>bmwdTK5+=AmFu(WNhUkSS>;Uth<+bmf*QYI=*Zuimk(i(2!%^1@$Ex(qz8Fk5@Zl=_$CF=Z-FeDoIIz|@YJb>~e z1%3B1)-*vkN|vLBY5J8@q^#hkisZpO;LPptT0&@yoZU3a4LkA|IbB7tmw-l{ZUgIyCkKqUUL_QvI|lAnEsS|3Yv(NQnORO z)V)@B6VJ#LYdWjkgLJM_Gy#H#Nk6>mg?`R=X z$y^&JX+pZ`o}#n3s-&Jkir0TzXBujmkD_#@A&<266c+7O1+(Y(qzMmtrlfI(1%Z(; z{YFV9%{JUb3H!R~!{fpvL?4q2zf41OAHUbW)UY&seP%9yCi<_wfv>-T%ToTQF@2JJ<|G=d^ z%tl7o_ac|X^GW-Kj!cp~t+3R{47}HzTMkK%U93dozK^*y1$Z2oq7*7hfvJ+Kc@ZHe z-+3Kx3>kYgmcdt{lXtOT)^CQK7c?46g=(a=7t=_AHEM+`3ZHpOV4-sIJhUZi7~x_P z38;iSYF~;cv=^$Q72@n4N+v*F@2NyvE@dQ(j*B=!V_HJ#(u~B7HSCver6x-~JC|jAtt1Zw69rbTm($%%1?L0j zhS)@+MY^0sJ|ILLvlD}?fBpjgCu3)02O8M(A6sYp-;ML1j2$BbM>9KmJv|Fs3uiq& zI(v_(IJxPGh&Z)+mGBcq03esS?!@K7z(Qq$idtYqC~@goS!p>~Itdzykcg0@(-Zd- zw4>sa(i6ZD=Ep~G=BOy<$3+9E!N;h^C}^al{Y}G;_Ql8tC)6h~AMOjR9`K&JT2akm=HnQ(zOjb4(DLp#D4(Zd0lRZo)2s`cuFMmQUksd}ln; zvH89;Je)i+x<)qUthdvC{8y;=*WKZqnVtJ+u%X!+Cvv78N7d|vi9{1Ex7Ru3KGb%wrKeXRMaQSp%}pwoIZZg}l4$;fr`1MD=-m0^MatwBU1^^Cma_{OA*G?}E() zPx!@~bbSQOd~nYy`m#_+jcMY9tk^!{T8!aHf1JB$w*`wl+6s6(m{mH6P6``ko%p8P z&b@U$l9oNBv7QTqzEsg`Jq*^LXP&nO`R*i6Zv3S}V#dHlF7p#7VkU=^p!T5Yi&Cec zGg1{8lfMEhp@n5rB?f!KD;$+^a;7c^HZ>7e_YyH0K?7fgcmm_0QFZqKM8bzvAhOJm zV6xYt;0|191g@*{!5o8R!(w9BUZazF>?cBK<mZlhmV2Jm_|@PThEe|5#a8F0AeQUN%P3(yhkAq1>dJp=+(yZ>`dWDi zKXn|C0qnLNwxxU`DBRPWF zShhxCzZg%i#W?V)`QbS|+vQf}9ABI*I_1Qr;&DImmvgxq?xFL*r)2RiX=SXTwMzW} zRqKI%0L{_gQI-tCxZ8EwYi>uYRBDB%x+%hdUfp1@QGHOjB0O#4a!bapo}}WB?Grib$9s%uPH+q ze{J7#xJ_`r8&|;@1FB~4_$b->VoMd##T(kY^l2AAmioDzK(@BrtB*)@M>cUn9L@={ zy%c>zBHf-V)}?vZSrLye)iFLsA=MjvaA*D4HBH1s55(RjcN_fj&YO10TdSD!YlY}~ zIe3xYM^q6fP+v)F3;QQmbN*h8Br51fh>=;;=Zq7An^D;;n0E!@XW@j9f`Aym;iIT$vH;^F#a3 zj9gUp^Bfww0`>QQj{tb_$U!ln8~E@0Fl2VTabXyx@WP&I4x$_J{`FGqu6bfOd0q!_ z)5yVDzYPo1pNJUnwRm2S~MN!slOpk&`~Ydn&P!XS5$l zi1}>XX%{A@SjV*wxvnwGaF(Y6p24RGtl9IP$JiJ6TkNefZoMV~Lz8EDu(6NY?Ge$< zNA@Rssl?ORN5;uiMaKJO`3z8K`nUHvw0NnH2JYg>zpi~(2z|DR*g@`b+ZuV$7VeMV z5ECtE=&~-JJD02^cpPyqKYZHu>Cmu%v|RV&I{?w>jn8&>25(=wj2w=?xwxv8J|wm5 zT1W|NQ`i8GB|GRm;o6Z@<>`PHRj4|AP zaB&fk(6aCK(#mExBj;p@1&(w2K*;tAErhFSXwKrk<8aMW@mTx=?3MadVg%0(2W=c7 zMzagHvnX|4C_Co3Nr>UM<%^}Br5EzN_M+!&Tac0o^(1Fa!ryS~Ez=ap)Y=0fq#e5O z4dgr&@VaZ3I56Rw*=Q8S)Bq6c<*wEBfWg_G8G2>`L(24xg+qnOz%wt4-u(jcL^wFU z8jaFFtK{@2WI*%;(O-gctY{X0w)Ep+Vc`E@X(k+gI4;TQhN{gQW1^{q`0sw&i-|D`RsoB4pU!#6&`wxx(TU$o34WG)OHPKjr0pp&Y z)mfu(I)~$8WdOoNyugN-`5;mefqoS5g5}k?laNTd;aKgm1TIE+>-(DKCe}m5zGlbo zHpZi`*loZBcK$!~DAJDLPy9Om-4wBJ}eUm7SBDR1BJ3u|+G2O*!2qcc>4^)r{xodENKZaNMkSAQ*M|&SmV^b9_}$ zkc*%@l{vN$`S!yjvzY$Qw@)4J2b2qfeql~Qtp#Nv1T2Ue%%^9_q;q^bVH%TA5b3T8 zcG*c?N<{gl1Lqa++b4lOH0Ezn<{BPe=+$Y=(oDlgo_p!jf%kEWA9{)}mUc_nefNfk z0mPlf`WZ9oy7jslyMY1E|GB#|wqHUhPKdPBioU}fF?JgQe*bIhKkP+)gSWC8 zEw!-|(K>O|bvobET|Q^=r_;th*;9Ox(Th06f(w}J7MH1{K>~yBrJznBb)g^+N5fsC zm?eY}7$#i-nk0ob!VvdBJs1fFV#?H_A8K}fF?b6NV2j){k{Vw*#QP4hn*xdcH!f=A zIWL7zuv_@xLD(NdUj~*bMl$msP6awOs0#+*9e?2@{ubQvh1M8;+^_S03bb!+`-8`n zLJ@&%td%z#6D@imK=QqWTWW5K)Td7fNOCTkjDL_9vmwl67O$-Q+KE~?D7TmLHUU(* zWc>1L$TSO=Ajo-fEnqIe23z)dV5TNvI^v0J^!#9R5LP_=kif<@5GdJE1&Z+@GI@I* z-9Sr!`RrF3?Pm#n_Jz^pf*>eNk}}A}Vo1fZgs^{O27njrfQzUX5b{-E<=Hp7*`&`m z6)a(n?B;dhxkk&1aWKj}^D#tjFtNmxrE#<_LGzk@$yg0tK66;p=7}xH6sC6NhV=kL z^5f1aTw@C`Qw>#Gzfav5|hVkzrbFEUp{{EAeP*@hLKq|!ZThnWiZ z7~24$gFrCa0NJ_RCsY}+WNl&{A#d>8X$)aE9r`R2Sow8fmRVG1Y%2=F?84EcjT@a} zE-+n7gh)vVf@_z^6L`o+ZV%gAH-fW*luH%5Bt_Hl-o`T&v`N1g02wh5?+FtnXFezj zY2~1BA0S*wt6k1ZM*55hi{!a(4|Ec5JYExausOY?gcF{~AmfL=Poun+Q^+)!vU9 zp_fg+uS!?E+UC^0Z3mdV!ZORWweB>pw>;^4o>3MvNhB`NLe;L=k6Nk+E~LKU z<9}#}P6dd8Rtg4msz2x#%BIH=30dA88wR1qwquBXpB1E_=>L>|>!<`tF-V6d&4l^O zEvk>MvWyY47y1Kw(Ut?(KP*Bn$3q7-T^DUOD^B zq{_$v0DemV0Pz1?6|=B)HgU9fH2L@ZSIt&BQx+~%rI0pm44bt@k=G5fUob1?fv75$WzA-UZ$b71rjzS~((aPV^ZESc>gt8h?tvocchfUTOl8N!%>*xHr1WePLXT`g@6lXC%>n&5 z&I8qD2GC$Upj^u>?(jP5=7%Qg##OXKI@1-)Wu4cyK%#l_6^bmnyKjywLRU5Qxi?0- z&D=SyWdtY}f3~u7ax96WD%D@Tx%Seec@BN!JJgR!8K`oiKQ%Cg= zcJ>Wsd<`(p6jSF^5!vr&t@(pVAPWz-7V9&aU|_dzWEwd8q%4O5w{DzhDfD3ICB zGu3>m0SX>|fK~jz3EAR>xyUk)rbvJZnnxJ-o(-B+XzxakE0X_R`D)%03G8U|hW7QRnGIR#y zAqGb3KiV<(7H+Qv6~AC4im(;zSs!OKfq8{80s8fMipA3;R84+3i#AGj283|P1Gjek z^bfR&SU;gVs><(|pI9#k!30;82e(~3w9wh{Bx;@I>=3TiU~x~4fR_No(uFb_)SINA zWLt~;o*E#qyJs_QuK$jZwvJx*__}5Ee(zqM^Xztg_)GS$U91{;y(;nT!YQ#HCGSKt z{cZ#b=TA~;{Y22FCz-)Ubb8s}+3Ey9XyLreyFF<#J2wF+RfgJ55bYwGDJ2P%7q4KJ zcOUzkE=@7j{VDu(#~WA- z-;>zAOBF~liCUCljeS6JPi8L&@b__(L9IC;XFYm6Mke_9BKhDQGjj~AHo)U{ao zgR#LOCEs9i_5_NQvx<&wxW~^hk#e>-ccwQ|QFm#nQtfu&j@U22qbVoxW@>q};JB7= z%mfWq)aKGLR0uXvUV!>(02Oh&bO*_9NNG8@|BUjVXTFPCDMtKtmHE& zQHX&DsR-sAA1*?urQ|P3&Mu3ZT&#MF9p#O-V4YpM+b%FvW)>AnvV{Yri9K3C!~;ZN znHhmsKfZ-InAugc6!rzc3-bwRXOmRJwSNyE?`TxF^>ENQvH zA0Q^PI_}huZwQDot%5HYp7Qo<2N61`C~gg^Jcr9lOWQj{&2Sh#LuzWOo;GncOFpTy zIbxXLeS?gnbxW;0{&39F3A}=I_eK{)DKX&K)VKldyK|JqT@NX+ZoHs-t`FI=*Jjbx zrG*CTxaLKPyaMI}&edi~st?oJDeq)YA{3=mh*mE_3sKNC3J7Vrr&{h~NGo{nQ|=E` z{&2`fOe!@wPljdFPo|Dmg_E{1!fZy`GLe9j@T4F98wzA_HRwGaeBhS^j!ve_h5 zegFn!gGx5{Bv=5xIT@AzT6LUA@p&;6LsY8i1*sj6oof>3(m--TEhyXCUaZm_5LCCG z$_Y$4(A-XpARF~PGFkB4Wi@~i-H#Q!i@LJcj{`~<5(87bvFt7%biRc%K5N3jn$_P4 zOx3T89Y%<>qd8gb4@*K(Ws#c{jo$ zC*h2E!`h$x>SZ&;ABv|&aH@0L4fiZoO>N3kkZj5%L*=sAc2y0UMYApw2LuA*<}1>` zxk60*hMOS-w*8fVuhh<#uW-7blzmZf93^IotIp9dOeH{mUIK7Y?2Q7Wy)bIXKU}5u zY%kdVPqQM}oubfc{{^l&8z`3W>W@hilD}d@%&}{%Mfkcsj`OZDJZHDH7!pbKr`i1R z;>Z3+>jvYEGMLjU%f;~Px^%0=rj3Sk5l6((P9G31W9;byzi83kpESfw^Q#sl=IEZh zY9E9tmx!`RIONaoOEvsHzr3HPi=7!aJO1-hpI{_%(6VKBjNLG`HvVoHg81BSvLo+^ zp}+iJ@1IZfcKp7b9=~w@@QbhNege;Ow+^$>l}&Z0V_iGlkDWeF-PrSo&+xKsc5-Jg z_ccrBd7s=pAMY<=ulW3eVeUvxLz8LFT{pe9o4#0mUe8NgxCOPzJ>?`Wv)1|0Z%zV5&0CokV)ewQI2Vd6VzBwe8f-|mke z*Ijbo6e)wEzF5gmU0L{XfszV;@UjRk$;MnUb8#vZC1Mq>RQ-u&`rvtZv?#uU}FBS|QX`jCvTy^uwdhH&wd<=IbvJ%Rlk!Q$Gkre`XC_Dun5R zlqFpL)RPtaD5;`HGW`dZETvUk8+&X|fL5E*n_Cpu8WZn4<6>S>L>X zPdY=)kO+{2Qa4Kfr}qMy`VPFiLefvPyF1^1|2V(r{0pzldmTcK^-#iZk@C-N!dOy) ztIgHLz>NoOK?-}9{suEsiGzx{Q3G3#&3aefh~BUi#H zqN{5i1Y7-|f*&xM{q0wxa$q!>I8RHDCm$MpfRlUR%Hl=*EXkN)-0XHznz}Mm&cdXR zDe3K9>Bx#4TGN_FEHdxdg2!l~9-MxT51pr&x-b?Ez|^oDMgYyw2gld9`SF3$yI3|> zkCXuf5M_HaZpmoJ)c_nU1L^INXxvPb%Q;awG!3v8+N#$&?h=z6=v8GW;kE0e!M~h5 zjL(~<2+LJlUI-3SaqKA22>klqMp)X~&fbkPnA?5lKN#l{ifHi%K<@yVu#@fHRW z@_v6v8h^OWjDdLHjO;m8)<2!#;D&dEGo7cOvl0p+xz67#}DdM>NkqAdGPcnk!F$wn|}JeUkM+AmU3`X#P279W6${^jHk z=k&zXH*>U#;ExU7aDgL&ZoW)^TkhsVma47c8i*V!aKkhcv*T|hi93@6IZP?BCNYYu zZ}hSfpjs6y4?*s7i!)~Zoq)re5f;MG?6nG)HXh=hQbpmb9$f(xqu{x2J zO~k-)k36W%L>40xG>kUar)!@;S~%>ZxSYXWnO;0Gfz{=AJu!=dfPZnrFhu>@++FKJ z;%K2T2i`6LU zlI_6qLKI`tweJn4Gtn`T@ zrdEQ@1Nao24|x|8I25bZ2tU2abn}=-A*FH>zuOp>R8e-STtr6XnPAQwsizK&0=elJ z#1#?@<=!2%qg;K;1q!KEGcm+QzYO-zhRRj{U9K|WRjXs>#a!`Bm#k@lS5uA%3T6E| zH^DW)uiSn`A6Zcc&tGQ)g10@Wm~)i{f5iZjh7;MK5+XPJk%IAm`t2`bOU9Rs@xoNc z9{GcC&`0()*+*oS?Z-940rM6((?Bk*GX#TZpwd+2ihw0SoTZ#pZ;zWR%w9trW-S?4 zj%SF&dlgsDMiRR1d`->C5tl^9`4sh9dDi@MM#{BliKIj_hVf;i|~v zKj^VTN>J}s{01{IoYqekE&a=vC5sbEnK_=9E?ZVI{Gu0FR2c4(2MJW>4d`?|7_`lR z$I(g%@-u`xf!^1DK35-`uQwz?KEt2a+my^;fwF8q1q9PF1OzOVO%)YefN01TOr<;b z!DM25tl*?l7HXFnxFfJ1y$gV2nORIa?ugCZc#t;n0Z;`@p2XRN(~&`qt{57y+Tx6n z$2#pBsmE$H?K~W5qUz?}!)Xi>kY9rbFGc)xv{wwN&vEu9&p2YSW1|u4uaeyRxxjTF z!cuu(Z4Rt{nfcVXJ-EqPi02n}*<^OIuL75mal#!RlKpvZ2L(EAeC zo|%4$D`{^3qBGYep$-*Im|+!{xa``)r7FZM{S}H*3xJS3NE76fc?oY zC7(3?67+(<)o2w3MH4ZBH1)9{KEf^leEJs&()V2QhIM$G4&~7~>9>!Y0eCUcmc~aJ zM#DP<;OA7F>0=`>klrE1ITCcK#W!e=3isbf-BQc5?MZj}i^FQV79D3^9KQYQQ%HU$ z!rU_=gWL-*%8#aG>oO_S3BcRfG)o-!cUCjPdY8LVxiM9*$T1+|7$9D04Apti(mqC} zrXRnFP3(aAnbxYy!j75ejMf&pd?=Q3bw&Iu0KL^J+G%pWQG|~2DdtImM!L!8lC1dU zcNzpCT}+`yY?T$8n$?EyPuj~6SIzCui4eYkDkX@ZH7iGOjTTtIl8p`0M^?AZ$dlPF zN=?VGoy3NEW>hp`<(v95A(NTk7S2b?P$5#FxR@1YSuA*0L^C<1y@{8UKDS%GwSlQ< zdiSMbKzn>JRux7TDyERkZt&h)l@cnxg=_@zc`X=dNPYy3-~FYdn5?YiLvfP)4mjjd z`NSQR?DX>?c;lk}Dvy?k0tOiDcnl;Ed~rE&ay*1vO;x<5XXQEvQ3LL?z>eI@Y?)Cl zK?zH}YlBh4OPBtcb+C%m1UmD>GHa@?K%${{=eQB3WbcanesdlFxUvS!NH6O2b4(@Y^d z7B;L~{{{N?&JI4JyC+*yYADgO8d{FyW*G0RH+V$bC1d8|ZPJ^mbvaf|&p8?tB@WL#KYJOA)vMX)Yp;7-A?^_}cox9v1 zg$AMf^6V3PAY^b9I(YNnP|&;@;*LpvZgVm|LyW_GZcE9%!Tyx)s2KjSYoILz<@9I# zrz}3s9?0vu7!!YLzns_{&?_0&pLi=(0+hB*XBObn+@O?JMwtBB-inYs`FxOLCq}Yneb?YhbQpw5O=m3F^drL9VPVKilgo1K@SAP;9dy zPQV0tx@87-b@FHJnsncz6%&pLE1okfhtWgL=pM~PnOe-EbE88~W`hij_2I9PG#LYii|)cw>99KwurD1fQjLGR zE3|(s>tx(0Z)?p6u1?(9w0wQ<*CC_VMKBs*YgA)sQS?Ya^*Z**MG z>YUjGX!8U=9{DvC@I)CCl>vraFZip29HCDC@+byavgM#aH{>TitLv{5_`{nKB{+K} zBbCzNhYQxjR@FWu!>VJ9(Ec$$)%6Z5F`QS5=QS0TAj_^r6f!ZEu7%N8@<$BY4V01b zfPhwkm2-eu54*nbZ^wR!)=`MW5b2lQ3u8V61@I=klOdt{N@8o)^3daeA6CZ=P2 zwF^o+c%rpsSDJ|x{J7`i#=SFsX<=*BLy%~9nO2^5)V2yRjx)ZcQqdD$6~W!7gC-#p z&mW5dis*I$Mg4hLTPuv@A}PG{hsWIeMnbwaeb?4v`IengQAl_k`D+tn zV}?gL-je|&o2y;F0T7o}n7fb^0>(A$Oo7X8q>3vtL!GdB!jV~Cn4aM0PRciF97Ee# zR{g^6v5c0CAYtIB^t}+^)<&!+1r9~)Ip5e=Q_!&)m{quA+z;6oSC~byHSYXC76<2% zM&eo#-Ma4IZacmoA|%c6kWytdKR24F5*xMH(Z*)|S3on;yUW61s8+qygoG0qu8O?6 zmPF?qX7i1+0XNuyKbjW=U-0yA)gZBDj6Q;{IO} z-+#LyXrCjUgvxB3klHi1Ds$dQANM%T^cr0M+)atLTC2r;k~OB2=-=txu1#E z64K-=FYn2tyf|=>6MvMWaQ?JpsF8O}1+wpBq*M$fHP>tAP{834BOWyLoEf6)W6X*f zFZb~|-pkGO+^>BwezWdZ&^X{1&gEh3E_Dm#*^Uo~k;VfV>`-=t)X!hw)#e{q-_-r7 zuc9!rmIT=!Wv*66ZV)M{NnwCGI;Tx?GD;hw;)c%RZ?#3 zep#f>G!w7M>Fqu3vx6?|R_P3Hg-=L;LOAxUjxKvaKMG7ut8}BqSJBr6i#STGgO)gh#1XVtW z<&1LZ=EM6g1mqSq5T5W@MMBLIdnf^alAacxK82-a z9$S?K@G9OcZbYL7i1ML#OsWo+0k`b*CZOFMuUGr;4v$4+k1nWc7z`eHq~`swcll`IEiwo`Z^S#V9_Db_f|6~+mxY_~#$vh^T{7U5*=4E~ zb5(7IaCz0d#W45e6m2WPD&|fZ{A1E;&WD)6R=?pBefc+Hew~BvLXEfy@!Ucc%T`v- zYGhqi`wv_XqdI}A+FiO9C+)D84IwS3am0_nQiHKLW3l6O5t+TJi#{M4?@g$<>slI(na{N3*EI?Q z(ch>WzCL9+x{~PoG&<{o+8FA_x|ey;tRLRs;GKBPgBj(tQHppKN`-mm)Vq(?*e}sm3IeQ*?1;KTn^nNVMVGb-5a7 z!~^;43O>dPq`8Vr!G^?Iy3Z;vJ_-P3Q+0BH^0>S*m=-RN?p06xjCpSXrEzmg*75oj(;q3D9!m;SHsH(wRgQ5$@m zcuPRP(48>sC@7$*0P+N_c2!_$MybF^_Y1L?1-Z5vb*I~S1eLepwW}}Q;75g3P*fO` z{I9~{4`f~m$uUTq3PWpz0`UN2Muj^C?Ru7NWA~O51wMeig3Wjq9AIyI^@h92tnJE1A;(gH;vxq?sMK>CvFO)_`zA^yHE%9h+ z5A$vd<6ah!((En4gK(X@$=2bOW+al#KbR}~fExA2`)#}MT-l!A>blSld@}nzX<0PR z+%E=oU~EVc0Jf~USg1|HYO%XH?I)BMqrmij3xCBP7n|U^E@E^RZYRPObj%%B5 zH%3#TnQ}Klv`6}Yk<2|-6_aUMeYt-$5T3Ot)|51qgr+z`uI6f$g==Du=fP3*t->{; zmxt>x)837AN1ar>g&s%Ow!w|mg+iF}x)BfXh+yB@fDs$l2b6<|H9n04d@z zs}dxmb*G{_bQRvC#8v3yGXo;TW4e)F@flt_`Hvlx|$1%ZUQUCgDG-U1{HZp)pakh|+IMk2(F)-fFhb?jH3OkPd;#<6@l zP^oB4Zn>KWtkQ-+32Cxcs9_K*c@e;zES2B-OgIryt9KZmXHmN#i5n;4L~G*laAUSR z)bvrL<*?Q0rPr}mWG)5m7P;t!`%gTwr$(Cx!QLB>+CzZ`$U}4H~D52Raq5p=954Hdb5-t0Qc(TY)4_KTe%HsZ+P37 zN1g!lLw~+Pwq=~ZnAA?2uMEv3E0y0;Q}!We5dP#EStsb|BC1_&{6$}9nyp8+=aH^@ zFW5bL4L1GYk-kPqz*NRXi4Gqt+-*4#^wYzpftu``!r)0tk0VvKR!(9~V5LNJjW87x z4b531(@J~BT~7xGLvU6Q$$b%8G2UsZR;zljowP@ZxU9A)mx7jF7Bj<4Lci2P`4sV> z^({2}na-Q@`XRMv07opa1}P`T&{2)m|7+*eAq^t>+zKUKDk{RbH762R2^N(8jcs5mthvps#DkB02*$eiODSl6QMb#d z#6)Hg%6Y!Qj+IU8DHsi?g;(d^{b7bw)0-p0kkYY)WesB-k&n86^W>rhg)3OX%*Ekp zOFSSiw;=2T>_V$HtL#CB$XR+HHru+g9?NoBtz8{)DpOg;l8NEN5~Y66V_&%9lL>CFaqq< zXAcQ%f=_f@k-jhKKlRgi5L=)10eSo{32A(4JcWC%$)r}nZ8-i#L=4IsYTA=_MmDu5 za*;=y#v~QP7%kz$(FEmFi2IGf!{JL^m2VVc53+pxgy{a5^46$_9u@o-f+hCDp!C@j z;NFa~uJ4|`DwR`It>y$t@L#Px2RP)3@71@^jsa9pqv?wO9tla2!dI~w(}!{OjTr|d z5Gc1kz(G4UScM3Z(+V+4%$<@Qn&41=T!??WuteZXmo80&n^6cUaLR3ZKiGg&NMA99 zC5ELxl@-S(J{NayU#sh;yR=8q1yvP!pXOWZN#2esa3_sEXFE1jH>?bR!r7L>m{~o55Se8_#aM#pOaG7|l<( z#$t{@oaa}4-%AfMmW-wfJS7zpVJS?3-Lu+cA2!IvE4g^BkfBRp#SmmVI}Z0QDGR=y zgJs39Ih#tZ`#AB9nAKa{nX{6z;#}M^6&%o?7yPgyr7c0^^YAFL{>vV?th>~!$AG)l zaFg-J2NRL8W^o6eS6R5?&y4ndD_!z{Uz29NZBk5{@>??IyJ#?kHh?Lqgh0mLLrSGP zy!?T_4 zTw^kjQ;f+zMP$@FPo$6=8b3hj1mO}k3!8Z27p-YxP1cP}zA%6qP3}J#;`-4@ycD?G zJ{t(eIF*ipqE4W=_r36C#Ng$Ad9w6=-0BL)T3}v2A);t>c_KmPjG8@kt5{c54NyQ! zDwn&&lTj9R#_Po*h##^uI6v`LDajuI=7o?WbQqFmSD)D_uZSF<*bW6oqKU9`b8|&& zg~`x$!-^^vVc{e(UZ@dW_vv|pIm6=fFbUJN|GXtoeOly^ZUDh!of=|OxVtSQHen>#{l0TSco0;CIp{VCK{jTR~;hYUkzLWf1qx7j3USr5WBTADR z@H>;e_yt+@+OmN*DF2*{JN+64g|}AW44l*rVboaFpEjxU6YcqpWK8f9DRzaGWCM*} zWvQc*SB*j%0OPW$cS&d_z!OA0C)UxT)rV>yM~KJBBleDf?nOgO-y$-9&xVZ_k_tCB zBAAF`21dbK&dBtE#aYAZR0cW5i$8a8$*}br}a)3^qd^G_q`NAF^C?0yY;^$F{ z%Ek4xyRpkNUl)e~t<(@_?_icZ&8jr|{0^g@Y<^TK_!iD5az7tAUY5#3x^+e(QM^{w z>o?W^RYHTSzgVd6TBuVkjL|#Kh<;JE0_uACfBhs%(w07BDIUPT4+LVcC&P<_tDA7e9G~;atq=Y+5uY&Dq4DMc;RW|utHiCpFLVHuqEfT zQ2uo#dfYk(UjG2P{XKRU-?E(Xu&W3xbMw8dTxig|j4Q=0{50c7Z^-HisKD#k{7Qq8m z2-Uo(IjN{o6*7ytkp zCjUpO&eqV*(#+J^d$~hky39jcm3w73dhZx;gsCm3d=Hb%9Kc9 z$@t-PB2+ySYu%sx@0jmKUUUEu38}(OIi(`-HW46j@H3ojYX3jPe7`Q=X;EfPR-H#?wT@9>UUC`_Sg8)P$s zL{dpjO^=U{T{~it9XcYC!E;Ux63mRyO{P?vu_z*EbvhdXkngoGeyfj)NWZ6nvo3X?&ekm`a^q@ zG~&_k6@96mnZpYzeLsMHdpxtpU}4zd9AA05^%ILR(~&y@K91gh_5O0`0<}Urp6z`< zmu2mL6PT$j>#*fegqa zV4#5!c;55C2NKTT^76eSnH_nuP>*^hz}o12;@4li&Gt#TB~2V((X{uX5|eQM6GksQ zo;xt3%+v{=V^)UykNJ~RdUt+(P`}Rizi0btReAh?c>p4D4BUM|75?ovB6T1Q?P>S5 z^rc9}O5-%Zp0P;TezMizK)VLyHk7N1xkYa|%p&>jG;8(!66J^h6Y2DJ=pr0|Gt$B7 z2Cp)K^)pkL4*P^uYAhN!Xzgw&Jf!_Zx(xXN0B?j2hNMR^6wZu0kO*tRY66A%VWppy z6m0Kz4^EKtQx*KuYiZ43rKTuz?|(_3Cw%XbfnyUjb!JKBanN`YjrGLCM1akjk|RV) z;%PkH;0?vf`wsy$CyFd-z8{$lhA_*a=E37m$nq6~Xq#cy8pHfTfjIWi(f1DeXjt-D z_zbLz8Cucj`Je!^`{8JMhGf%2tOpQo)V`!pC}W3UnIJF8Fc)?93F9-ossPXR?S$rh z4a9K*0ld)(RP_80olApt=mweSdV?J)VFUZbo$kFcTvEvda)`jA9T)5xDC?>+)q-1B zit`cWPg@nrkArJ=dZw8WDAa$@3~}TQ6^U6aB^*tXdXO0Ji z1N3puiNXTPP&?~9=1o;-xl!bRFoZywCZvMq>es=yqgT1a5@TC}lRN@BmWYuM<&zX; z*jvczD?kJSs45^lIVt!9$IK}FixUN`hP6bbCo=4(Nrf0V>mTtgep9t$@q=}VUISSf z6W~1v{1TM&$bqUu_M@4)MI8e~i3meu0{b;|AqL@_v?h~)@M;q!7g}fR6v~(tpZV)j z^c*z|M!Iea7&3E)z>#W{>Fu!-SLFJpDRD3X4EX8Ah=DoSPx)4YKI9Vi0onc;65%8e zN~HpnHlIlN2T4(+UG`7KEkAOR-Q=C>KlR6@h}Yo6j#kJ&2tGkG>-t?x4{0xKf@YxA z*64Q5q|+>NZJ!7MiHkGbhY}+}EL9@B3s}h(1<1?Oy89NM^5AE_{fbl6i$;|v2Nvdk z)I2f?^6sz(2Vg6xQP)Bvf6WLS9fji$#(tVxKTt@|A<*I200gX>NXvi(VBXsygR-^A z@9oh+07UTrOBL3MUiq)(-J|%M%^#SKfuRaMmay~j?~a*!eosvTI0=QE_!9EhC`YC! z=&VP*P7@#}MK%$!MV?3U9 z@=D-4u7`q$ps{`6p(NK{681{bOHUd9xEm3JZV-?=Vo*Lq9y@#q`bb}aBGW`Or&^s4 zECH>T@xR6jz3-=ZU!$Z??l9nyT+=nD@P+mzxKu(M=3$cvv*rVzV27(I(?lm{$5g|B zF=i0^RiXS%Tj06cjmUj^Uz3&3Yy0ZgzrgU$I~wu~yAiOJ&Y(RSrj{Kh@Opo@a6TWG z!AK%Ebj-r9d3cus0cLgaY)ei!PEfV19s0*V#oDk`SP2Jv**je(8C3Yl&WjWEQzm>9 z`2b$Likqh?Hsd>DWmyVtIH&+Y2-QTL0U_uRNm+=lq8q94M-&IZEJRJfKbex8Vkvm4 zO^Aem(#F9>kO@PbL2r18&Vu|fm>=MkM3cES!%<}zQlq(=XQC)#EeAp1LiPU&B#Y@P z2Lh=RJ}kHs0qQaNGeAK?f&A;_e8M9l>cXwVvoI$ToA1M%_LO6+7ecyEH-aG{gvi|b zwyYsERAUlJEz}yw!(j=u&J(V_j^aNfosi5e9E)}t#8A%OBRfQ0x|WG%gR*+ZS!)jJZ;w8g8SuMg%`bMZXvq~5S|(aAQN+YNp7L*4M01FY*&#C z3iy=fUug~?#PCKKy|B*wGEY5IXqrz8`C@^Az<{?c5iK?tc!C8(z{b$RGK;=pQ9@^{ zkh-#Cr5qJ$*affZj?p|>VV?wB3{Ohl?JhKvIlUZjmFP9CoXt^#{WitHgtQ%q9x>(b z8cWZIJu_9>6zGh;}kOc_MDFc;g(|8oCgP%i?kVdY_N&Jc-)LhJT zc@1#ueN9gRt@%oy99}NQ>b%L2w&L_4*v2+$jY=UjxK4;(s#kyH&9bl!)cd)K$f~F} zGNFS9*>PG|rMuoTutZqoqt(v~oiXe)^9DOD~ zch-A0bi7Jv;plPrOB<7@mmBDn^;(Jd{l_ovaXWEW%}fdu(}2%=&!^}t$TIxame{(t z>MEED1!k_<^moG%YvCJuBX3ET($HU#ifbm4k2Cd;QMzTaiY9Q(g@a`7~bLV=^ zY0&{XTOgV&G7 zB!ny{D1J3zM%g6{X_Jh_Cj`%6i1C>mhs^3FjA~X7%ZIFL9-4vr%SDb%Hpc;UDWGFCc6;7#R zM^3o*Of~CRNJAw)O9HgqqQ#LUFJ3P}Pgr)ZZeMPNxZJm0 zzj&Uq=*XY+-M3=$Tjq0~6A}ZFVwP8$HID;lgASbzp_2O35 zlZDEYqf*KH_Hhk=xrKrk4O^D>`{$mkmwUAo-K4*(C%Z8=6px#*s>IR8*$=@+HyvkV zhLN2M%|;wMoxuJQH@6GeqHKMCc&s>y4sOE>&R=N-rT{t(#*xcnd*7@c!`#fxxe^NX z;c~;Xkb;LQiQ$3EKE+y{u=#fD>^kjR2Oa5zcdoPsvA%2=2e6(j6SQ!D z{`Yh`POe?}2Le5Zl&-38v;kO`1VmnG##K;dv(AkX!Ndw-pmvI8^5&htYxfc(uG_Q6 z0>p@7qG-i#!|lVCYc{-CYeIbn!k)CY2eWni5uoFlx zAr4b1TIFujV*9H?mQUqdNaf&=EA6lwDFgUqkeN!9#scjK3R`HYIgU+J-~P?M*>DN8 zlh9Cy5tcTa6L-lbC)~QUcUI1B&N>yNcb?j9+i`K=9t+C%FWNUGA8gHo3$^#EZ&)tn z?#BqMxw(f`LalOb&!T|044zGayVBsR9I`PB4)>$;sB?$eMQ5{r|Em6>I53ff?XRRK zZ;Dp0x&U@d3W&G9@~UDntmJ0uqS{bL<)yymI9>$eDVy5)GfL%u$P91*5(#Dkk_yp7 z=Cm3+8pFnrpM5(X{G3W<=UcWHAotd#ZwoK@LqG_eWOm)6Wi7?h_AlRh%44m8aJeym zA+>#H+7pCltDnd6df$^bf$@7?@e=FM9xCQag5?f98=ykQ(dNU5nn#RWlaGVV9&hWD ze8kpB-A@CT!-Zaz(BeIFqKyu*soGdDeBi|eKSOQga-#Gc9VU0O+547rt_i!m^rJZU z`kH>-6{JcMRY1q1Aqk3~)Pe4pPV>#8t+(jZHGM2(Cg3#DM-O$G`&ng#OO24X)Ka}d zbL9YxAx0Owp}UnB&tMhI9NCi0%sc!gwsZ^9yrr)oSKZ?KctACcl+otV()`KYkBc~3 zR}+ip;$k1sB+U3ys(2{-F{%FRjj70b6=KJ{Ku4FRsPwoQCown%K@@4T)ylEGJJtXO`y3^4zrj7f6uS86Cn&splN1=x}X(!@maQl9-Msi^K$+8ZB|e z?}}lzch2$L7+wMh`nTOJHnC1KasHZgY60se7_0qF!HiIo9b6 zu6lZ+dqA!a^%S9&-h5`TNN9z0=*_$msPT;sWOW$#BT##O_3RO+FQI<+hE85Nl^G9$pvv*A{ZTPYBdEI(34+Yq2Uu)88 zI|xk*{?H#oK|`#sL5nS8e8&UcgQK&9`I?PG0Ue;A{o?$cUn_ZX*iF7%Gsfuh&>vmb z^R=J$cM)D8ZB7+G7&O!fR?2`_dAx~4c9!d0Z8m!=#>(4-)_S9>0Fi%?;;V$VG=;q%T3V*gl z?KK|pGwjuz5>6rR3DMITZRd>-4rFBionqFPM{v`-JKZ*u7QJ=O3hh=uc5&QpT?zk;I8}v zJ2;nTv697P{Ka?4bML1g1|WD<$R}d>tqy{Bl(gPub(gNC=i#nmzBd}Mg)got${w*{ zP#0c+o3XK7^mq>H{EnSmWr_xbAh*t%LjZ_xg4N)6{#vEbnEsKVi@ac?7b;xsV0#d{ zXGC$8#xtx8-dM?L<*QDrT#mLys(t_LNhfS?;{7GCHNObs%>_#jJ}RTLJEe%EHxAkN z9Ih0z7SuiAj_(zehACfa2r|cR53BZWgRlfA`*~2Ka!y-6>CQYaBp(it#&u;DjPEXV z)Zq0_wDLP64`O@a%p#)x+N0Opz-;-BS5d$(wz%^mj|1EaLC-wTqMpoGc5U{?6>Nc- zAqKbE6&9*{MVm_Dz4&Ze@1q4{um<0!4NfHzK0TkAzS!1dR3A}uzSjeMd3YOYSQ{vD zTxof)$#vRBM!WXa&4#Sj(Fwe);`T-{)|`4)=pIv=VlWcV+Rd${Ne8yXy!i%{8*2RT%5QUkva}SJYCuf8l*<6f*^b-PH#R z>)4LrP$2K^YsO*Rj$AvK88 zw8Dq-v9nBFrzpbOdY~kvZGsM`G^4^RH`_Dg+?0kC`0-amHaSFTtn6)N#Xv2VJ$bs{ zULh}GU3YOQ&2MD4pu;cB1BOthk(UnNA0i0&y=yrhHiF!MO-I^N!Nt@K>QK;cpSmzrH691R(xX1 zUWi+W)5y>+x{&L+c{*&`hwc;_0QwT5 zlS0E51e2Rysn3jZ%v{n@=vC4s53W%gM$HeNb77EKo*)??GSHrra(|usX2$GYc!&EU z%QOAJJhIf9WOWVld0TNmEinev9>ra1W%l`w&eoB(kT}Hd8_X_f_QR4YS7#7Es;81t zujDpXP9gUbT+!0waF=u?uC)t#OTqpjY@{X6xT}ocb)AU3jODaR+4DhnC=;w81EDKb zMLGjqic-{pwqVrsAQ?Ox=xAI_zn2VTUQ?E_|BMpTOE~LpNW7Eegd>m^ZN?a=-S^hw zmhvE3>uPE2B-+Nrbu5m{IvimAPr+cwJN++wmwk!B zA<58AhkBSwcYY52m4~P#bWNQh&&GqD+H;X7fa7 zmeq&GqdDy}pV(AiOkH>qYnDgGxRYe#4}=;X?ji0-5ouwo%UZl6EAH(XjHkN7uoK5m z_58vWk(4?-2CN37rMuK{w9=4wi79K^@x8B*i4&paRO|j7|H<}gC=ipCWN9ekD9^GB ze+VbAmVfd`*K;|nqG!wqLLl>w%pD!lRX46KbJg?Fp&3fB^3#~B%YK{0$yEPl0GoIE z{kfQm?aeXr-W5fK1=J>50WOW@w&K z-ssW<{R>yjt9RV#_UT#W%zf(wbc~pH&ZtL^MI0BL`3Vs7FOiy9yu;qOgpW46XZK?d z+nh42PglwXjcQ8Ap!K)4TfJk~?}QsPX7ocoe0Nt{<59tDUs}6rw`Azc((Old&ZeG& zf}QM^+g3~5PYX2kdGAcZcb>RKOW4wtYIHw*Cr|oT?a5zjcf@nbbf2u+d9Dm*(5Y!5 zeH#Iab6v0&UPd+NU8{J>FIw?#@x91mi>1sDKMD)#G&Rr|qz16JK+>miz@Uh3 z77GB>(Q?&oA%M`)=Gf9DmnS468?FOiv%Z*g6;VVqnvhdw!Y2%W3P1dO-OSEy(Bsqp zi8~_-*cR|f5waW6^3QE}3?$bMpDU#=JiDr)qM~Kfp8EMr%jx0Zj=JB2zoKd*NGOLc z`De_CD>R?WrL6?gfU-`2)?&7)VUcd%rAWQksUFa1MMIiK$tbd@Q5{X4au8j#i1KknrfO20i8JsUNBsQPFRQh1o2A|62=lu zLsyTgg1_CU3cdX}z8aarmnVzPz08sn-whzE3&DxH zsNJ6bo*X^}sPqkP4qb%90|6Z&wGCf^HOwiYyz$u2m4zD5)Cz z6el$eeILj>E?}cn0FneWRfWocQ!5#_pD|E9j-Xj0UgJjTe$^*ePo}1veyPVH^`M9k zIH7FYmQ=`1nv}ft^@AVw)|0~i%Y?s;o}`KhwkpvmElwDZ#BquaI zuY=zK^+K-*a<29MvRO2UoB-tFdBOh1_k7m zT@WO20JgpF4(rlXnqsV^@)G;(Vtg7FzdPE{9Moxa*-Daju%n`VWDj5n9P z(A_Ba!Hqu}kQCH*XSwbB=#}>f!SYHT9H{u=3;E;diLFUz#K*WW>G!3il z%t_R2K!>}KDT|Sj`nDoXqJYaB&2HeCo2g^N*P)=Psj-Af|KVVoE7xPEkZSheM1%b` z24IWRgA~A=CSk9V`BGNVIX_UXEIxl1888DV0E|6?ir4-7BN29 zU6*e$3%OWX&Fa`)coq8yuV&fLiSa`ta!Yl;uA4i=zOgT>u(!}MhjK)A4dnn<*@ptX zvMVTy#yWhuz6kW|(nHqpa&T>S@UHIRl0$%gZVRFG7A`F7h7i+f!ABw98a)VPj(HC%9^lO8@_{WnJuTZT=}oi0Xjd zHU~`4Cw2S80NXz6<_&E>O}zw|?R@ElAzc)KgK1XmiUo;qB6CI`J>|xcNBjsDa5RZ> z-0>0dj%HI><)s+ERy9@p=YR&hko8eWnphV}#_VBU;d;LFJfIOL6i+U@U8V{Ki(4L!e}@1J)nKrbL9 z!y@j0bT^guq9PJWx`C`7C^{S%G+e7lts#e}o`pO0_j_YNR<}{2H@$i8xzs=yFvw2; z&=>`%Eq@Z|K%V#--o-y|obMD6K+iVpxO$4)uY~voc&Y1nhQNCb zIKlb&i;&E;1T>}}7@GMGheS4|(d^QmlkJs8LK$=Nqb;KMIBpmoVOs_rEvxIGP6)H4 zvb?ec3%OVDGXwV!_jTZ$O^c2Rq4LNtC z0mvjD`3<9-=OnsC-Qd=GCep`i;#89Zkp;o@r~dR7Q5y9@Do|ecfmfZQka57J+QAWZ zC}@h6v7j6k*vAhoVr6{0lfiZeZd~_0%X2pSUU1CxWbFo&nP8MGPD74A+n4B;F}q2H ze}u{v=i$u-p(16{CUoKzjHo1S;+Y&0O3K(Rb4g+f^9{wtRe(caa5k^yxS+l$wcW+> zq!Fo@_j=G?CKv=L_CC?+=;-9c39oj(ci00Pu7Km|JArZ$g}ab`fmKyA$by9~TuVtH zoHmTa&kAceBBG&7NAT+Wi?fFE{E{;5O^!4tK+`|a+5R9=w>)fPN15$GrRN1b@j86w z#Y;Z6$~or}>};muQ^O741Zz9#XttI6EWUhYD)$bIHe-YvN$3bGMfW%idS^8p^>|A? zzGh1;eqjF9NOVO+>DDw-S1Mw|ra3Ci=Tk7m|9RvXb2u@)X#0J>;F$fIzJPLpWlJH5 zOkAFHHoKKYrfo28Ah}?FWq5B@k$0x-5(cf55U_8g+=GZ&D8PfkjSavgYa<;U$`?q;#)NSqm(H4Jd^&O)E zCU0!S++HT?pN4 zf8==k<>Bo%a6d(?+fIb~9U{=~q50|dL9qFVsZc3Px}cI9seW@ z@$nRXuPUrJurF>vf4emW+!;rpcU~SBzpziB-OZps<@|@jXO+({a6(*p5T29+v|~a< zDw82pU61_=#JCQ0QLYjppO=cwj6tS%IkSrSL^C$+7bB|hp=ff$gRXtBfw*-R*(%0@ z*@N&tYqj2DNO*P+l`>hadklZF;#i3N89CuYay%reX2>2y2wDlIPiJ}pPZP~w+?LC- zBnWnj8lLDT^WWH*4;tT`mXViVvm9sf;)v|%G^^<4b3K!9S%;uip^%GW*fTVYUL``t zU_~{W*EC6LKSL@f9d#}q8Pl;!Feu3iE!@DF^&fNWWOWElLFi`_GLg_ha-$@|0sO0i z0i^@HkGn`AbOa*E`7&)lZ5vF|IhiC=O&zZ#*12p<(a{#jB?(q_1 zV>0j?bh2~OCNKJ1B5&(hM~;qJfjM45m=6DWRdp_IOO=Wb61^e)aP9cz3(+hJRM7vy z5hg{(qT#@Z8$-W!(eo}e-Wy6VmpcBtt&A;cn^dSijX!u33I~ibj2+9>u&uNP?9hAQ zX=9mxpr*6uo5vDrGX76fo<{pNdlpVMXRu6nylIv7?Z*j^ONytE`A&JB`60wysP4`}0=PV>IivQm9%%!qxAgCRDEaqtNR!2ZC$A zRH0vM$S9H{-eUY!_d&Sg1Id*7$>asS$g=Z?3NbrhQ0#j(AGTNBCHoRQt>Fq0!b;U%pex*IH;fT~dAV9$?{*t=ClE z9(v+fDUmrJ34Vz+oe_Qj_+QIRswd zl$Os1`d9I5>d1>q2fkxIe|!m}we6ns=i=ilU%T+dpupMDq0n2xqmuQc>)KhaCFUjQ z)Kw%eC^kCNG?84GB=mtc97LOm&tr={0I^kGtP}i`_mI!$#mI&IHE52(7Si2^^2Pz2 zk)wSCmuvG|KjSM?e4otAF>b|IP2WHMBPU z|MUBO6B(_C7!X3-eK73P)`$&JIZt1Uj%Ei| zC6c?MitvyxCf;kH1#VeZ^c@wv?NXS2<>WeYPef5;uSe*#ESKKEu;m^0mL!DA9n8B| z!srVYNlG6;1A;?8QdmB|q{d+>x*^^uFA}^EMoqX;Q-HenL(th#lhIeb`kTj_F6J9+ zJ46?Bo99aYzn1EhZK<;f1^{4^82|w5e_eMDzcXc-5%@r@ zX*1`xuC{H@Dx7}JTcjkWV=79a3lJC&Bf<-kgY}ccE`9eoiOd8s|E5_rT=>@XA8y{A zK0C?m`unb*u%!ZxJA#9K2E`&(}Ss#(uELnyl zWx0~#Zo%ws+XQdprmEO?z=GnGx*^yT?mV+m)H5av(5u^aGyh)kv?DuWC{Unm1L~?%Rctg&$Pb6{E+p+kR?Cy9dqWk##6+M4pVcs&6#F|^j^d@PU9lT&FJ8NEP zqbR(^=R2m}DiN2y{XE;y;`o&@Dq8D%ePX4It_tzlC&kv`#qV>i+n29pC#*TbrjKoG zncKzP_`v1b;|8CcYP^dHm18pQ}TY6^hh zOA4XxREWGmKS(^f1n*R+a)&(rE3Na>CefNZH+tblDfBWVEe4Wvof2{l{y$%Pv{v#ACoW2cTXFCHIDV zyN<5oDv(3|6e=(KxnGR-qy>xh&+D&;$)0u`IHu>smm9CA&wO@A`Z}+n*YE5%HwHXi zKt~3goL5*zQOqu--q)WGF9%jfeSN(geLt7av^8*yv7*eYU z$Zs+AuH|-lCQ?{IUwKr)2F*vum*>vGSxdJ=aVyqKGXk8x{wyBaOvsp4-uCBqhM871D zDQ7H#LpC@krW$Ad1vj_B55xUp8%m>IiEnU#2a#&v)X)&6_R+h>8R4gXI{;36Cr2ZP zfnQ@{V&a8KRDVu530fpXk_vPh4`%g^(nqTl_M|23m=O5G>Q}VvsVtqPD^J>v6$lL2 zpEVC5^!Y#%T<39DQ39nkTe5BamJO(g<;zVBsL6V|4v{_O-iTv$shTQ=`NBvnu5y6E zz+V}mQrPef(-6QFX)Zlh7II^}kx9u$n3yW<*oDdWHDXIA-OZ-IgR1+s20rVq8^$-g zH+hXtvv{y$2ADNyRd2ShRm9day6?MKt3v3AHJIM|o#!B&%oi|*wvjU`Tp>wv4q@U~ zjQb|@Z|r*YPon*b5aQq#yMzjgn9%B0td`@~2r{$hQ%2ZhcQXmD!$~3Nh7j~T5*C`v z0aYrORq<{z27m=cVvvboP4RN;Cy^x#(FKgz+-UO=2PhcRTKh+SZH7=yKujEYI685M z4mUFecE{rf6&)hX#1d#%@;T@Vz4Y1W%u8myZMO!_d3>C(*n${UsWVHB|JWeKxwX2( z01%{tcZJ>5!ck}{9f;36l|h68zED(e%~Z#i?E0HU0m>WT%s?7t6wFQ5lgh)jBh6|n zWlnUZ!x5B@2B2>HkNJ?AKh)kCj7l|ts<(pS?jzYJBY%r)u~n@|v6t!<;)a)e+``NV zOL9_0rm5;LFD-XjF<_7CE&ooJ-+EtW_XaGrKFGS-G8c&tp)T_cfP;qZ3x^mjUPOV4 z7U#^}G-+mu^6WyKOS?n)Hx0LiA;J|`)k%SD1cA1DP2q3C97c*PAOd(uHmOP)?H8_V zjCNR69$qV-MWGLQ^w@D3=axDEA{IS+p40#1V}m{y?vrkaK@j?E7#hz)ny@{jGU$*T z9}~e9_)U0|cRo&;bZUvWWSL(j_w?bpZOCSdNxpvGO0v#SdN9B6vW_gtwr_uQJ15zF zAv3vEVsejyWsjY3K|sj6e`(!zr-oZ4OJKv~5k6`LmTW5r~40 z%dML%h=MOt$Q`(G_gL<>L4gZsQWn4g2fVQ{+|>~7Q&06NeQ4f?TN$s2 zy-XP#*rz=MbMqBbfxX-d0}<$5qL)D8lKO}KP6D^1ltJp~{TYF<^QbAcYigFT{nswMrnCJq{ zeh=ti`9g2JjMxQDED2QOEZvCiPkV1bc_8vkQWVs|ORFI84po2VF3R80+ju0|9);#y zGPFx?$*fY)$v5P(uCN5;C%+QfRz`&KjK7q_bjl_#ft3mcYIu5F%b)5;qXkGr^i9@N z5kD_2{xkjD(X%!tjMCydAv z1!ZNasz{;e>K=?VVyT{VYnO)w0#O7O-9K%`V%?&>nmzbUA}f(XrT(}Gl{DROIRmwJ z*r52JW~kcewKO#)Rp-NuUav1riXiUL$a8|tCNd_yO(8SviyTG@n?lZja`&`BfJp|~ z@PKwfm_GT=l+V;maz6;_0fbvz7;Wbpq64qyQ!3a`Cs+Kv$f2edfT>=yWAgX2Ru@E4 zoz+O?q}Toek6q7+GGdLPXPE_o^oFQ(9XQ9Kr-bs)vC=&q_%`}LO_Ze7g`~jYy~1Ca zlN|{|#yH+9(oZjHCpcy?>o>`@0lXNUXIQSiX~)RM8coz(3#yf5)k#W{3O#jKPVye!e$`^ParyK0LVW$PpmF!S$HKJ2{%yIBdf&ImZ>iskS-t7B$ zF|S0`cMTSzB2{5H`W^--mq|CzzNLP@`;37 z|0T5g?V}vVc-%6#ITN+f;UeA{Q7tg4R2F0wJY*@8X0Gt`aVq>MER$--k;sg(SkF!g z*;XVfy4x)zEYJF#d7pKf{<)Wa5M6Cf^x&8{f?jI=9S*ZZql~MV^QBmyczrn$QmAw@?Iwg{^hMM{lSbxRXN{>a>u4SYM+!W@X zXZZvDqGw&%{P@3H<*2i|DgvMZ0KO>yk5{RwhpDlvi~auq?sv3o?YG+ze{S>#xY6TX zG@&hTgdMeQ0tg&4_vsoO?+e`P+kj{l*{o~IkvsJ4b$?%HVV;GgHe+&%Ukilv)QC8< zG5H>;a2w;w>il>=BufjI56^YUq==moe!qaZ zyU?zB<||Dy+EGI))#_t02h_pAJ!{lxBp+Hd*N7z3byO?aqSH#4wk1?AgMv;o^z_wQ zq(WAstzQ=CSl3$iGid5IQ_P?8b&Xq5V+X#PUKD9J^`+Q9#~i!7RLiSgHlQ@=j!n7U zs4ia#ifKlob1J2hBThNLdQn@z#FA8MsPu+_E0SVpm|GTTDOD3FXiJbgzZGq^9OM61 zo>tij!A&7Mnv3W@5Lt?uUjZ$?rOLW z^scWS!jV>zF%YwYL_gb7kyZpgl`9ZAFKsuxS^%hQ_9KfkCQ#z}Q>ylhb|P*#+rug1 z;pq^MhNJLu$=ioG=1!!Gt*UK{hm>x~F;4@U&71-Z5t1Vl8M`s1P0Bn|`%W7;M^Oq| zw`84Kl_|M|pcOEWePf|#zIDAi6S)4Ae+(^y@&fAwtS@~w#k<$?tQ+$I@1ICmP1RP(&B8gGwt9wBIM>+GGFzhGjDnfa*^Jd_NI*sfCP|Gj%B6g7@VtoP4nk!ZT85KK8k z?-yx}4V}j7mpKF}<9zdFd3`^Vt-X!5s=;LOt`@-%wYKFjE<(LN_=EobkyD zAiP3`>>A^ySO=c1!n%%WQ?gpRGV2gHexe%~zv}V-*gA(OL4d{xk8RuDv2EM7ZQHiJ zW81cE+qRiaa{6D=hd%VZU!$tNO63bUB_!y3dOYTra4R{1-Rvu;0WEis4 zuoRkB4xx2oS_Qx^(uoMQ)#@ZB{us}#x`NPkg$cmQ?)iQ3J&7NewD>n}x9gLI!^h*r z)!`Q*FTb`8GN>2qzBieAa2`&f>56BwiRaeMAt^Qr_q#gEEY=7P?1wi!Jh^);?5c>m z#g@In0Ge2i*2;zgL*ONafzt-d{J;r0Z)}Cz3Vc4}$WjMxdQ=nqvQUeS3M!+qR3b?0K-B2< z<$Y=tZV)Jbf6<2e?A$uBOmB(^%4!{_3^h%8LcbpX)8_VL@?6{PDoa9}4hrrCJWSHK z-#>7~!7ma*Jk6=E<=AY*U7<{7fme9LqPj^JMk(yYGALmR*=4aq4uQA39%Zn8Xg++5 zpboM6Ue&!A$~?uYK1%qIjcj$nU|^TtJt(ggAj8YRZ0hO<;8e#0VaJPzTex3+g?zw~ z0Wf29QP`C%!qYqUOh@qhj1O09T}^>gc!mDW9!c1U2bYmTrp}(=h~cSNe#uZW&{PsN zq>IX=2Q+O8b~Ahrt;;na<<>PY{rPq8)f!Onr~A>*M&v={v}w~KOpd>Q1{wR9-aUrr zxnKXRJpVKz`dMM8*#mp`lhFnxKTG~*_^ch9dpx3rOxz4bHiQ>M6|XCGT4MrD|EfnT z+GCCFdGH2dm51$lWtT|DB+q>onSZAHU@V%gK9fO9@QEh@0iACU=b~)dE^Yq(@ja@z z6-l_-a8kO0vA;*7?$k)=LD6+JevK!5gg!!cxnpc1s;|xymSGs=(??~F+@VV1z<=L- z0Irmo^hQwu^9AhcCx~%q)uSicKCXAUhv?nkrXT_7OdZw0x{clBK!935pQ@Gn5t1zJC7Ta{F zzTLZj6?Yp2-9%F2x8g)+p&$^OCv=SisF$wL3!tAe93Ze_d^4Dv8)UhyrWA!v)!*R2 zgcl^^gRcezhq})t{n>5FE3gF>rJg|#4LnCVcxkG@0Y4vT;>bPOOsH&l-39CJCe&Al2E5^DyHz zrFkX9t8830^bZ|nJd+&Mqo!yqQu|wiZE>U<*d)Oeys=yZiX}(Vw+Z@XHM;ac&sO1V z#K~7z)l*lNVjbz1MT(j``vi=b9+p&4Uh*+ezibx`D)(FWz#3|R(qJ+W;bVb|t_qY@ zZx~8j!lU6{#(l3qx!qhmC0cvEoic3eKy46QEyM@z0Z@4>Pve)!uD&0e>zhgN)VmfOl6oUYk{kDfVaDXjI*V zYI)C^XXzbBfmW2v9ceVQQ_81BY)I?}c~z0CTW!HtmpBtjh;lM^xNeIMY_M_b8->Sw z+4L@5tg8rx<&PY26d~7(2ZTeZlF?j;hY``u5en>_Ip=XvU|k)|u;_yI5gM`-^b;2% z0FYf3A_&80B9#b&n{`O`8B);235`1ib>&hUQ1jfu8f{b#)bJE{8}3qJ?K^~@n9V!< z{>s4lNk3xd{vZUtkeBXMXZ0_KNV_DiFhouGOfWOD;lOq2f3V5w-Jr>V9UlH-AROM( z9+2s0av_VL8@T^`oq>lZpwHuwYg_M;OMfmB#Dz47n-b-tQ#Q#>2JcRdTq?7R?*theg5{ z5HT;JWftGHXG*MWySkXe-X3Af;B}F>-5K~hy!xso+7Cxe%aL8%_7|86$Smj8^#-Rh zm5Y(>8R@_Ag^S|MYUO-6J}nu_O$^d=bQ z253kegx>fhc)3(0}m!Of%7Dl@ACG1~A{Cv^n z^~QE*VQw}CKi)J(l}XZO!d}8C%Js3++(?UBR5`CvO>C@IE4#BoQNv)k+@-mUM;*ZS1HJ`L&+x5L#a@=?U zeF=hi>o-}l{<9wZ^>l`FLXEre*-S65BiHW%EDLK*A_l-OJQa2+x*ODl8QgSMRX z*pr$`cIG?$r1zhKlAjB9(*!I4KoH~qmHz)nr7*RyHZicb_;2YS%gXMs?Y`3ww4Rcn z{nQ-)?*;$|L7GfW`=kw8R@2&mi%XO6`qo%tg&2}s!tYK7A*BM+wXu_06PPP;1k>Pt z7snA`=g-^E<9>0puxi9$l)7=VbP+||nnZtLGrKo>R|ZbqV%J)#V{^@VC!5{xl6Y-No#{cvGTf2b@NgK;{c@mZg)I=h>G9Th$}Wq<_n?O0px8Vxg<#` ze(Zxv=nH@cHpE0E8smh&LR5B=6Ut)OWb7#`J$G7zL8(mk2coPGG4ftvMnwuJ9_ffG zgsQpy@4%+EC#d11)GeXt_e0SE9-Be?|Dqq)ul4mVaVfGJ^iM)@Ebf{CyF1qtQu|^L z*fasE2<^G%4Z3GHk+mSXtR+#dZzSXT_6oJ+%%=c-u{o6ui*(a@yps69US*ZZRrBrg z%WNyF=yX`X(Q`VY0-)x%yvELjaU+Xy`4Fvb4gB(oG3o-qc_G(LRE?d&>!qQBIWK5y2Z|zf6N*kUFX#_Htz+_nL%(v z@al|PGc+%%P96XW&y+V9B|x?dr0d35Bm{jet`bp?3fmA%C$MD|IY#b_2HednJ9JZ; zQv^c_5q7v@adCVjx7)}N%ye?Lfe`P!;wOW7SS4rzZ5ZI|L9HDBXsa3``1`&ShBMVb z)mH#o7_D5(f>78jWP3-wN$He+lipT0<%xO_h^L>2lFkR+_at@u_vxAb?T}Ag+7Cbw zjC@>*OaO*>#Y%zGJRT%t{Xob2l6?d$<4xmRhRcj$CM+@XZOtWyU~(q0%W{ z3XN+>Skk91#3Z;tuuzjk(1@a3;FouX2ec2^6|#aXbBKrVh5#5*1kGT|>SnR8$>_^J zXX}`eth0ZC*fvsGv`dv+mFvA=3(yZ4a36TEp0MgY(6C}_#fsn^DYwQUo<&0`;0+=4 z-FsNz*&KL`%A4$mdK|az)o{H^eP>BTsr9eX7e)b1J9LEqf?t7TW`Na;ePU&6X)}gwkAP}Um1dzv=1v0cA7Qos8qS~V%zU%7T1&Z}Sxm7j%5nUmisc!EG zj-m%NAK=gfS^jy7KN%`4;4vSLBZipEO6cj41;Wb`CN%(P)vd8qu@dmJuuOL6)ZP}u zZ3dJDld?_7)G0f_t4{Q_nB0GjH$#ZQGEJTynP?!&qxb5LT0%RsB}0O1U>(eB1kj!2 zVmx#T>MnNxsbLLLw#$*($ZXQ(Sz$lK`*x|sG4&0j8;h$t0bsHNx2U0@DD~0HR9nWS z2+|$bjH^6M4KNOHh)_<3CWz|MzHBG^pfzIWm)O1fet_h}fkiuJVI(1;H7Xg&CQwR+ zmy1|LFr6*r6_EE=pw^R`^rGA?5&hfNL!=E?sw@mSbb+)>K-Hrgpr%0X=s+BgT}wH( zK-dt@hHzfVnKp`HzVfMvtEq6VD8S@9-bGvG2|#w5ZV4weJ`{|Bh${KL_)hf=;aFq-?aYXTUKsOojB}cjC`2l(>MVka zV^9zUhj23V&MW88t{H?mlOh&6hvQEh`?qyW7t>Vam_V&$Qpo`-uc@`w(eu7WID!{_ z2L5(#nQ|j#27F}|e>y?-3$08rXwllhpP{`K4 zaiq+_O)SQv!r@D_#i-p>VFM^O$O~p*!U8a$;DzmaL1H$Kq3+4QH*Z18S!R9AOM=mf z3;qNcSSfeiZt_K*&6$AT+&D99XS&rq&#EG4gbVrZ+V z3l$?JB1YxfoS_?ctD%x0swXu&_2*F8IceS@GMnA`_BSaWP{SBpyrG8p8TVWDg9oXG z{aaGZK4}I4s-Z&$ETnYBYg~Xd@2zvY1z9|HkURUwt73Ej9s^`+w=!qZ6-EvzbrwCd zlbd!Kk6#&1vCC7F<}|lMzv_#?vu*93ej=prwe=Jrwg*J=_q7vIb-vR>JkHT>z6B;HZf8!WU-Y` zdBG`M?EvzQgLfk2!0i``&cVS4R9}x;o@F_LY)|9_U{=?3steB*(1`}9}Q{~xQjgjOTkEM6+ldaQWDO?WBS|l=~ zqN${Y0W2)Xq9W)B&W1us4#FzYE2F`V^l2Lpou*$3QrB$s(x@eTblge-?}zXZ+hM6x z`ixSsvnid0X~BIj9U%WHSia#8<}2Gr%18j5bwN?oOD=RZ7_+z9a~mcgsz_sI)U>I! zEoyR-X4062*C`PTe@Q(J=ak*J&od2OKj@pWqF|k5fvv_*GOiu>o zx|P6;}oP-x9)7OCIhG3Hpz9WSc|)PjKu+CX1wHF$TJ8Rsx*DYhGsCMvapyA=k4xwn1<%6z~9PfG5DM|;-Yk@ zp(j z43Rq)T)X0$Ufd(Jdl=`$Rk+Vi3F${%p)gKUZ6z3mA@usBywkvDu2W!R&NzZ&l-b1Z z7G{Z%j^AU$bfe`-H)|g8I3p9(oa%iuwzYpJUwQDXE2xRoG^8!-<0}x(y13lHys28RD!{?vZ~oF zubQW$&a-Dk58T?hrf}=E!)hh0-uRh{n~(780>|~(TtC=ZgKDYvbqre~!wj=C6@0CP zL6bSpV)tAgrD5{$OsDIFmzHJJKIDFD2DZYbM!Zmqv4mXaRJf7};~V5)M339AvPbM_ zP(IvNP}WVYmS#);{I*yS%AC&rykrg=Au>GZn68gaJ-%K&dl*6-NkqI4q@ox6w(H=g zZ_4g8lw5s>4qFF58?LB%t4@cL9zZ50&4|YzIAac%e|0VY`2@U3=AsMxm)XGn7uvx6 z|BPy8CblMy2LCJHOsm`2U9h73%+%JcNCd=)UVpQ$s1`6go1<`wzY+zeVXt=VaTnU&>3rsK8BOhEa)!kkJIwC zt2IL}AYS?)(WEANcXa#&yh(}Xs#CE;u46|HdxClkGj>!gpG2r9tXuaaWCc%~l4w?o z5J4Vmi&GKzFGi2JJa(fB4$oUz@SsIm(;!r$hEK8vqJB@ADsV!tUz#dRsvi5|-h<{- z-di=B%X+W=9CI1amf1*Y+45=2Cjj4ELEQChI6vqv>jE&XfJ;v~KDkSp`{#D;!ji ztQrP^sJckj5pXx@az$ByVk!t0Ap&4h=0Ah4c4NRZ1JtH^A?}T&?Q)MRtAQ^ zxBsxYd6IM=Wiv1k^|6CYzGyM7`q8M33v2s^-8qN{ncAWSs5fVOE)}ANArbC|HQ-si zbjj$j8@)h;O)?N`oS`e3x~AcciB!S0R2TaejSTx1`13m(U3Sk%w79?;b_|>HfLmMt z5{=-FIa0X|JIB|{+5Dr~w(hP~H}KtP&Y|yO8H^|*QM7poaH$AHU^`U6KD_#TSX_!g zcj@W=*ejlC&VJsemp5kf_l`ZiYjU${osE;zR}QCvo5{nt#2F>jj!fJ>0IUFEOKRv# zx7I2v)6WV;ap&XSb}u*8G-6ryMFu!?M)RcM5x$2{Eze6&bUbn{hxZR=uEkrIJ%RW6 z>y6nh|IhvK7|!W;20{)}f=HRcn%QwA*$*2FrGZ+dKE)Qkt1|C*sV)c718MKk(Qk|y z+!-lLB(R{H=m)0(A*&s{0s;92)yp>A7OVS>v^`*~^4AdS&30SXO-MNAv2o9SfNjnr zo_6&DTC;-{dj56pezD?3Ik z_xI*4oE{kbKddS|zeU(NYvN>wlgHY`?WR?sjmt)z0c8>kt%LTi(O_Wje7V<>rJ1(y z@U=}ItXPV^_pe+Z|GgyNv3Ljzs9dd8eLCab$D%E3YE21Rdh@G=C6L~FMY^Z%CJ}56 zG-E2HENgn$69xRp&)Da2aZGz&hbr*>(nsx%d9=&Y>xhw*`8*>hTRdE+DCpwhnAPXb z8OrW9Wsi1-WX-0McG-c)N>)-=M#<^=t>a5Ws~S!5aW$fs0*Iqj% z1z1!Ek($z)=|4C|ehmPgOcO%Dgoz|Nkuo?yT2v5fh1j5=7JLV9C8y%n1j8aegTpT-^-+~PRT{9NovyzMRs?Y() zFtMhQJty z>s96=Yv2qdAn6O}4D^#F@mP``uvz~hvH;v;I$A|jTCbLGdt*gA+=twgse|}i*5@HL zfj+`w6V6#f)ELN!4`gy}_n#-YHME>aB0B&;<`n<{{{MG^Ti80AINCd!{G0!MZv@x zg7aIBv~PB{dcTB&bX0a${u`H-dZMSL;C|bjRAg1wYb)$#CR)6hH%+D&n5j;pDs1ybHJUM57rORL$`r~L#)uj{Gi2@kzkG^y>mHO8 z)JXlh#x^!J9B6iP24>L@ya0U&!yg2uk6K-uDJHTgtO}s#y&20-ocdBJ>VA1Ujl$rO zTl#oo-)^I-dJL{9GEI)B89NQmxwWDT#&L9u?|^>jG;*3%as>OqcD5SEbtZNXk>?kh z((A1i)MdJZ%)CmC6asnb#*y~eyZ7NmFmcw8&`f8joC*KNIu}1mH0w90=JcmGbQrMS zL3{g3&Q2%N#HbtefCXmy7#fNz6QPO-R|})%%vNM6FpEJ2pF8ZTLi0W!)Kin`^@dr{ zNw)ZD0WQZCBys+un9U--vhP~V7l~x7sDQ#-vI27k$~LQLN-v5mGY>yY$Fd>PNQ{v` zJ(9OHWX?3QCXvGJam|EM131_F|8*(sr6jV&VhwA#Xwr}^9R{{yx541E(@532t-Opg zJwMoZ106g#Hrb5o=qPV&x4gRp6vs+Eru3`ngjiz;y}LQOx_;Z+yJPQO2EivqDcneaaXm0!o}|4)|>9hmB6p}?8fGVyH8iPUgHKpbEcje(~sBN!Elrz zmJSJeZ#G`ZCm-I;FEj6tLD)p~-$Ua1m6!uS*?PT{#RF3fvurJUQ3s-M?bRzyA3pJs z{y&*IQlOPc!=sPy4b%Q7v5_U9jOLXREKHMnX2|K|+CM6X?vs95BB1!iF#<^GzE6RR zpa<4)M;T(B{sZu&(Ps@7mxzpC3eK=j>_jD8iq1~Q%FweT490vb9 z9qoTqOInkxlwoPU)GD-8%xefu)Y-edc#W zwX~Q?>u`_#Q-Bb`P?rQP%`kDMTUHaalvYf-S!ma1?^60M(u z{ez_h4YuO@?xh&q>hm`jp!uNcpAsMf@_Rq_P|WWCL7_JgIG3Z3h60n&@z0m7^pu4G zP+iPE21OXRem*!KhVBnx!#RB)-Whi1G4PO9#HWs}1|ar;Qh{he_j)Z#(`>YW;n*|26b!9T`Or zGrN6xsm_hCL12AXiN@ggweY55Ev;ce@N2`skD>F82)Nk6yT1N)X6Ee`f0f($8yg2V zyWl3~WtBR+GF!J}FtG>iuFBJY z04^Q{pM7X|lHP0S#*!S=m6UmB=nacFMT#>ZaU0atTjs< zaRPb|czrjgOf9tbckH1QZ&#zV@dr73AjDbwY;-fP(bHRf9bBATJfJA<9en)U(%Tu? z9XA>|CkbEx2QrFA4k;_wLA*Tq=^}2Hvct*W6jQC=wat&s%4$CC;X!ncEK!T36cOmD zjiTR^pnR&R-E_hMCgK_SdLc25a?x4ck2HN*q^e=Ot25a=m)Jf;Q zJC112TvnAxY_b~^xJ0mj26rOg9!%dyI)CB{{_wy@+4++5JcGvbaJwQ4-6bv(FU;=2 zR|&7hgL&ssw6x$i)SxP;>Op}(HyB7=o((-#fvbxNcd|aKxYG~XPl%lL zj~p=zRrrGgt3e$`m0$+>Q(hhk_U)Wmh%`tHinBu!%pnWF=G^naj9rR({)~}eXj)kT zQxU^^+175;{3X_0A7C5&@ng9V2P$nax8UNa_FHyQ7OeZ-9vGqo*uwDQaS)PZciMUdLPn4${8M0!DZoXF5Tl#vpfT{oEe_fATGM29LKBB zNjv3+!Q_bgW>%t8f`IPOyL&FE+V}m1L-IjK_z@Z=EWRivc}U}im2mp}YZ_9+v^v2h zq3DwdS6BO5gJ;E7ZHXtnvxD-TK8pEhH+4i%RDfB%%K}<#I>Y?HjvkI%vEI?FsIAfg{Bpk ze4(XfI~5{krCHT#1FwP!NCZpyFvvp5eDo(hJqelCl2H+uzKt2k<+J}+XPFBv4y^{n8^@$n^&vni79|$O5)V1$FZ7t zQQ(6JUtr%61B@C&+r`g_dhFdg?*wEXHLm)?oAZ9(+x$;>k3-hXNvik!6c52U`?DV`+jp{DH)S{jNvB2;6PH8los6@sCw5 z(tM56tS=(iQz9G>1YXZ&k{83+D~r}Pg$Z3IRBoToD{7ND>CXi(5}ZtRXrcw*CVl0u zTv-=IhJy4deVJW3h?bg3C@KP=DDnrW(*sODJ@R7fyr=}ksYGjlOiCNCMnb0l`WUr_ zjR<)f0Bca!niW~3Jg+ooHiAa-TjXrpsV^a%wOOB4<;2p+LV2fJTgf2Q!-^?8oK9q^ z=$=2M0Hzx$sUMbgo>p}ypS1ruP`=hbtBSPLkW0tQE&@h}j6}85Wz2zq1xICba2zSA z63tRA^Hq>Fo%9Z(W%AP`$F=7Vt%AH!)ueg1EI>uU zAxE5qCGK)aMejW?a!W?Xa5l%6CoT&Acv|&oMy+=NU~OISp&@fbyTJ<> zwTNuU1TK0XKYIj3N(2q`cHU@o5PIbw{nsDW3tQFc;uXi9e^1;*6|5z6PrchP4PwZI z#rw_61MYCIMilmyZH!l?5B!m|26!o6oh2AvB4G_F)pHn?UJE=9Uao#c0-bg{x4P*kV5_tjK&ujop0kS&edm&b;fgf@1rJg;=xHE4PIN>_W^r znDO8-%gTms)y8JUR&Fg?rHy{R)QWZQ&-d-Fx82eQqB=bUcHvoW!Y3V3vj**$h_twb zjIs=IFyPvZrn2P0(J{52Z1^_DoThqg9fttCdXD-*pf*4HIEmGUfOmqnzJ`#dbm9@z z%ytk)utI#Xya3J59`F~my}li_QN5l!r@?xtlkeTh)MDcEOtL6n^`ySYOyhQ`V9*P) z7U+}NgODe)kd{KD2u%|zPe;6;&(TC?audD<)b5yUF7LH)oaBJlN75WpfHoHEGCXZ- z4W3@y4!1zW_U~4xpBwM@rXLo(TWHYHWxhq8uCh#L*&l*?(KF??ciD>1F`R3P2{A;^ zyAnZ#dg3jJtmEc;sT^>tP^1iZNm(zh+*3z>@Ig?p_-uZCYXwdIojSQM$c{8P-cKTlkZf-r$>kY5Y@XgYV8a<6;C|-wY_7RmOUsT7 z?#QY8LsyB|GtPkAC^#%i8_(>iT)ZzHl#WhVsO$H9J{(+UPug~hJ~tzPsq;>W?wh5r|4aHB`K|JX8|(+<9CL58k@ie$WMI*FhMo*9453oQAF`jAGzWoG6WzuU=V_jR**5y%0%&d7>!fj1QR{$$zIOJ(Suig=<${ef0)2gCksO3w9 z1|(jJNmBo)+DS~Y(KsP}>(C+6(hg=Ml^1lqs8+oG9-G<6&n39&sh1;by!v5``1 zD6}J+Nm^Ti1J7o;Yy@NY zsDueV+(O`8pe)A%kiVL+g1o^>J!&#FggJU*4LR8)1s)?1%JMk5y3C7sNoH?Bxp!bw zzjaBjIUk?UD#gUf#kJUN7-l&WLwoQTV&ho=j6~Q%~X> z9$`K1fok?(4fR>5H!%1~MKt6sJLsgMTf7`7<+_c)d8SMQ>28M_7=d>2D)qaNU=iwUZw2tn<*nW~t#cn)(6}S79we)otX5 zhU;71-=F8#-JbUqGBPhlU=kB_gsTbCDt3Xbk-?X4zPcG;zu3Mc^h$gy6mC3&G9)l&HH$Ob3_Ne2^cN($x}8SU91)j z`6^U<4>a91F!%3+!1u4qQO4S2DAN7{dIm-I&nL7UZoS4&-PXMUsoSr!DmxEJ@KxWq zPwR{1XL{FpLw|CCDD|oM^A6n@$;&VCYpVy21nsB(D<;roz+>bmmN>Y>uwI)_ttLE2 z7V97g-D)5Hx~GRgaw%d_s|xe)s0No+e{Do`xq}s+5_Jdohk#*`4oH!PqCN=l7{%Dne)PU7GyLkCsji@y8BrEf1HeKb7E*q=T+oKk;l^I|^l-#( zG+foG{(-Xkb^2(xc!ii)4VtV?JgTfbyM*}u_P|dB4F!WMSDVEgBJ*io83-+Fyy${J z^)tAx{s*iiHD<*|Astc7NArbogrRb$-Nsc0W3&MOo45LQ9LQP@is=AJ6*C{*1CC0#%8#Rm-)DXspoVPN|C0T2*8VK1?U#pu^szu4oJ7a877qT<-UE?h5s*d zUCC8Dokr3Dnozbp6GP{$Dr^LN>o**szw?2%>B$ zheR68d+eqo4^6{4e6+YL{>%6SAnm29o6R_23%0$~3#WmjF?e2g---Q8WR00Ugb*x1 zE>4I5#AqmQGg{+G`GEW}d!L^(H4L76SG^(PIk{ODhBFXIwBmrH4_SCDV}Lc3j#l!u ziSfLEixq$1)4I;T9*4BE#V zm7zZR&c0p@A@gEE=@54$i{wy^=Y6WJAqegr?(7x;mlvl@#r-`V1>04A1nvjYrL}Rpfc`R_xlwDlTI8^hR(Hs zm_cCr09-q&ZU9|X8b_R6MBf3pByEMjY{i$)t!lS98rZydKx;^g5#^8k_dCk9vG+R% z)YHV^Vb;hS(&qnV900C>UX-5B%?kt?^^**#9-5{rE@B;>Tjnb?C0)qnHebp;9xn_~ zBXHb=>ht7tkj=}E$ekRs8S4iG5;jt_B@24xDycH4(J&gXM z2TP+{UgHQjCs?sN=QRL%ic{-Qxu@u`oq;>2+wzjt>Y^{tG692IpqPQ9iVixA&UOMb zJ2eI*QFwP;WTXT~z`|wUg$b7+?(}XdK;5~_waV3)fe9Kp02l(W*GJ|;!lRg*;1x>- zEQAFXlxiw+(Ml}!e6NN80DmFtpZAcy&T4oUC8MFsj7I-~=*Ul~0iX~&#(b=3i9p6A zANqtY0i|b9M&b?yEei>3hFb2zGs)}FDZ>itM#kaqf8UPMJ?s$#P!PZ)mEo0a+TH7v z0G+{!-l$uY?#bE^>b;GxlC1*ho@v3GZBwd%%<*uWR$oJ1g1@Ui)py`W*K$6B;mJD& z82L3TS0m^h2AH2u>TS)=8vv@db(VfM8DzYnOU8uldat1rSjork+Uoc32X7)^R+KcMo6e7bH>Q*!Gi*gf>v@dIHtN%vK)$M7pt53Cp~C^1z=ag-IpqmRRj^@|v!TGK zHeUc-d9Hvt`U`A=!z!*cDN&Fnz%J{Y^*F$%V+sQ1N=Weco;|YuhI3z^9DZB+>fECn z*Y~dr#CdL>s_SD?ks)7*28C@&DGA>~uEk5<|M@i*#=KwjZ%rZNehq)ba}nhpS$s6fy0{~Q(hNZ{vnMkJ_SRFy#xgsDJmJ%$ zWhIIAq0n)Y4|}>N<}3GF<5v&<7|q`C%v_lru4WQWI)jbx7Lb&sGQMuI>cGaL07C46 zCMMZA{A`;nm3K#$J?t+$Ge1FX-%%SG)ze9Hqh0GE@VdU3?l+Ql1*IB7EGd)}NIgZg0s2XZh(G z&t1W>n~t?`Pzw}H9_BU9>pdXa>D%dv+!ZNOs>?d!!$6o+jDON{^K>ZgIS+J2?g zhACaMp3l6O2=9WkbSX&p>?nT#f1U|K;qc@nOU%QFUZ3vU!vz``MMtLhj}^la@49q> z-7}0cZmEm!Z)GxD8{#n5(tuX*N^cCq?IJC_T%&;t&u_GqWoUnLJEYK;fEu91MNvBR z^BI!gD?NL-KfjQpN{zkr=_=b#xGBw`O7x97>PqVv43%r7W4A#m;O70A+?jtP;N$|) z011vC_lWSxk*~|6a6xk_<}meDp0ZcH7cz;7CGZrhvEZbvZ4brn4W-MNx>94$-@-5g|j z`>RV-?Jb^otZo7^ew?Ri0duU)uFpYkg=%RBJ&LQp$uIbXYBEc2T#?(3%P8{Vw$ zSf|KtbkxhNw+Q^OkvxgV(@M682+m&8%Ouo%l?3d#a5#uL_%(f3CAu34&s3LSbkYn) z_}-&1%O_R`8x9gTEGIFwQ9A`)3GWXXhb4)I9m6q}3r;?cCerox9&Y4zIHQyur?;fG zoWhl78@yqJ6n(nQRo|0cLqrAJh*xhP0G&HA9 zS~{{s$U;s}xwPWoMmAqQbXWLvwlOT$6C_2(sret&23%$%0D2yw0uJgKpvKbRFOqpB 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 F`#=3a-*o^0 diff --git a/python/wheels/meson-1.12.0-py3-none-any.whl b/python/wheels/meson-1.12.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..0b764d238f9419b7d061aa1bfcf38123e08c8746 GIT binary patch literal 1106208 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%0D*d{jrsi*4aHK!;$B=1v9pfCJ&4Q+BKIx5pJ}I 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�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%0AkOD&>h{rQ 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%0D=gjIvD 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⁡rWdvVfkhx}9u{1|yN_p#AoVtSv=5zFynq{oqOCgq z!N0QY-~=8;1%nA@0%22+QEg@V{LzQI^z?k$`g%Rku=sV1;aV&qg`+MF6l*?)mI4Ns zdA*#E*u%&SEHk5OCfqCM@nrP=#b`nCHC;J)`XJ5C@6hI+JT0_473$)ovTs)_tI9yv z<+_nV^KPA#**byT6gvDVi%CbOjghrLnNOr< z#3mYZa?<-iTPhU8@9p5^v_eKXi>*Anij$=* z-a;r`P`PAjv+E6akk7Bf-0>_iW0R;xT{;*4vED7$@*UGDgn}>5IaSw?9Wa?w(M;zR znMnoy&h1&|rYs3D{dE@5Q4erN_qNSf*07Cm&^}nI<;WwSyksGS7vAUBJ{|#gc@kIl zQCR68*7J0V_Sq53ZrN}wZyL4%607nuS$o`ov8r(!EWFJTAwRe~U3Ld9tO%A9y8+#< zw`?Bok@5m_X{=p+y-2Eeb0}vas?unaK3zhRSK#WIE4Ld?@YpI!BTC^OEB67+`N1JO zo7H;?L+5U)RyZbe?x(cbcgYH$lm30A9KWXejXAZQXRmu=GR z#sS*z%$jO8XMegjsSpnfhS_A5h*zDVS~7}W`sLLsbo~8Y^on4bRL)*4&;$M>9oUa+ zD}04ZeM6BJmTRR?6r1;ZCiI9`GfE%O3d>4$r8mnTgruFf>n**dyvYBo(}9ZvD*Tl% zZo488CqPhuFJR>c@XrIDS=I=rn#4UMd(E{&`TEG4tzp2FJ?^X>YV2olAMFwQ@pI1U zlO`d(DSN^})&wj%{oV62GIaSXSulk)eSxX0HFT=9mCGn>ECj3?X&5)#gV$#)eA&w2f|#XbT0iY zb8Df+df2F|8`(f>Cs});*%ByfN<`Y4ZUQ%+T)jeJG7IV!IF(GV z+k{wk=1MBL2f)F>!H$E&gEV@p_hWhM!k1)9{0ev4X#FTkcj1>H^_#;%qpkUO)Khg?GfVw=W~Q(?-pL|y;;5#2vyw_CV%;<~jr#-equ6h@i7g9Nb8$?pk16FjYT`@F^x8Bb z@AmBU#Wf`jo#@X{1lC{Y2nc_s&g1!5|33UGqudXbqwm6t?qIs1opE@_6(9ZS#zJVfjFcR=X%9RPkIkt{N5dUt#O@D< zBy;+U?07~irKlqmU1GCD|7dhiC>i8p?))W(|-ggdO5j?!)GTqiG|$w1*pRO#y`bY zc7fe9QgkRY?k3TfAG#m(zX_Oi%0EirLDF7Bs=u*DuX(%`2SIp8Gvb2wfqY9|C#yS@ z>fFQYpgKTiQkJ16W+g;qy{m&OyRFFs=hymIvs|JspY)GvRC+{FM&rfH<=bIU!hSs3 z`3^Bsj&!f z4VpAFnt@iy><_vTOQx(2#)meClv>l4aX7Lpignlk7_-pZ7IU70y#ReaIIF}ZJrkqd zTxXirQ)o`xS@s_-l}?WJnTCE{27iI@{fvBhnfrQK`+jD=K6>7t;XhWHl@Hf<59{EY ztmy8=HB*pIo`6#Il>cI};+`#WZF1_L4&$h`(>7E24;yE6@aXHO9S&6<+hHfp1IPn&|( zA)E^+*SoI5@&nRPfXn;8B$f^+00Gb#O06;@i1~=Kq}GEdR*@UR)#N3U%*UyPuCbHwJ`qpCeu{Jk{OXP)Jc>!f z--<30VK>ohrk|CK+>#JF;HfnjiS~^P>`I_dd0;W?k*PnHQw|#Vp9}J1C~5$YLg?qI zUzdEOZlam!s8=%4 zRYfI-7({0N)7WU6sVh~`?u1=}-5HH+@<<;)2)F3`&HBV8*g2(N%**TZ{e^;Hf2W_T zQ2SbS(X07+{grWDh=ll-+9_pT;(28af_*z3stso@_Ve!=q?zg7H6Q8f>iR^MQa(ef zEmg9I2Fjd+e!wE@&|?3Xtk^2(Vd1wf1v@*SIPz^;MM%Krli8G16Bj7cE2Mi4TxhDRSTh=yLCY0NO?@Y}-U}J=yhFdL&Tp;|38hY9a2h$yV}#%_f* zpukk8StBY%YHvGIQYh+e!v-z~g@8HfJ2=Z!(pYie4S2zZU;!CKC=P8XvYi+uDq+;Kwgu?7GDhEf!F{M=53 zP@EVKQ5T#F$4{LMAveG@o&QF8NZI!oKq1Vbc>O;_?m~1CG6cT*$t5UC-APFrVu{+! zMBh(#dfg6JY&=Bpl023Zi+FO#?FJ*>R_7=vbgrLmus=~w7I;MkcKR7%ibA9(&Y0T= zFJ#P*`3&?E=({SK)(Qz1@sm#8;SDmy)p;krrro%S0|^{_EB2EKXMD4gSEqK(h3K?b z?78jwT0L(`n_;6@!mhxcVO3(2?;AYlNbwCd-p_!vE|vzsv-*j^-Sx#fXrA92NvLn` z>6JJcCYA8$x$vl1Pn-eUQcU>5=EBMFC&}XdU~>K;Xq3l)sD|Mu1L0$veE;#6Q0b=d zXmZZceEiAiCrP4%F%y#z8_yu^QCtyVUK@J;ALJhr641~7lJ}bnqh)z z>q$h*UWU;zv1J{VVrr}R;m>zx_jP+~YFqW*x75&QAdBeJ45mRWH85H5H}Pb9lhC^G zOk)UR{qyoIgS{#{#ShF0&JQzb*d>GvwYF*pj0HR80ZTXlQ5+(l#ufmQvg`~azM`M_ zaxFvPXDau*%&TK&w6NcLAq>yGX>x3oGa7vt@*oYOzLn{ZVI&P-mMwyE?&%vM$aU$9VHUTC2$?bY=u% zt0%9t3e(PU7=bTKY3VK6ip0r^b)@-bF`kI=3H}7-?su%S*>h}@|vnAcza^% zbbe$df?bcUHZ|S(@Q`-e|9(_|;~fxrnC104N=^oxBK1BGne$M&#)g*)!SBGh#RR-o zZPP!)rsg_|%#uoA#m;8DxXp!MhS-z1ndEPyrn6e=s@9-pIk>$?Rd%PE(#?xkb%rtn zF5watEM)W^!`=XdE#ctqp-p)Dg{Q*SMu4qgXk|VPy=Gru zeKjF>v$OUs`!b9`IlRx0KgBgByYO+#%FmDq((JhNxAt(RO;^uUboxkMF>bpVgO!Du z?)+pgLu5>BoiwH={o*Nt9YPklqPXLpw%`wWXXN9JII-1D9IOC1Y3mtC6AyR7mm?uF z+@g4Lg@}f#QRr-%f>vo$!q8ae3Fq<5rGks;2u0c0e(W&fHxh!$f9_2F81QW zk){h6Hxwn!*;RN4)fn{X*kaoSg`8Pc=~%_Khaa(x87ejLUJDS=svTJ9OzQ?}k&a=@ z<L@1Q#TBT@x6v^sWOIwGR!y*Gu?luQn!jQmBnV zfV!cmP-7jBR5$Ugv@+(6>tX0e6ot9zUwBH;AsNrH5JCxF+!zmrHY>u+>cAeU@X9J} zII}!Cy(k*m5YPtROui`R zI#^{K0e50m9=_D=Hlf7Mg)w`K@NdE5)LM7D_xrj-L9FTz zM|4FeMPTJ6`>BRtbqT!ho5flm+(`Av$jpZv6!%}P_pG1dd-B;)bPun5%beJNny@-( z-l{V;ETO4h$wTFhHZpASFVC0F=cx^6`}$tknu2EL(KNA0%^ zq9`<>@Ns9b=EqR)Vv5gZw^3~7PqpFz)OxB9a8CTZ?-Yelx7-BYgse^-!IVTtKZn&n z?wra>i>sp0k0zs2jRX^7_Dnr|@AB5vbktjUD{1f&fr&&(Dta;?lr=)eFQ!qvbHB+C zGK{x1ybp(llnzKiMN0SV-Jxd{d(h^If9)A%mqM03)Q{( zi>%G39EBPl%Su7HHi<7gu4Jpq`9VP@krC0D1k(UoWxwm7RRx(p)C!|1kTxN+C`&Q9 zo(9}ZSw*&7Y0Ukjp=cUP!zeo@B@b=vR!K$?QpZ~76C96eh_5WBS zcAVF!J6fH9?`mkb<5pz0Fj6nayE$e4zRn_pOIo#9Ck!^*S06Iqj50-=&@MAiatupq zCz#)bRNbivw%~3g6#JzRRwDZC!tzE}9uQ;&2f9lH6PoD1D*GNURpqp*q_#RXFU|>9 zJN--=JGW#kac^CRgBW&Xkfigjw+0*H?dq}H{bddEcxsUa6Gd>B7NorAFjbFf;clvn z@n^~{cN%3Frd -SjsCP8n2UjhI9~6_$h~T!^E{_)eZK2l}h1mlPYwT!0j*ZB?6a z_-iYe2ig0svND&~OqYfh@S;tfPbLQ(6HEQpqv6ogC^SbG+Rgi-Sb^&t4W;ZhoW+mN zR9_aU)TA)!qi9ubky|YZM5X69H#gKk#j#jK{SGK%5u=@sc!7=t?OoXzLyYxxNEm*- zz-sUOeS!pe<7bOz{$zU=-UXLx@H;ln9JVC2a+&^$0*<($*xP6KCRQfKW%3lFOe*Qe z!psKcHN@54txY|D3za3PKq`sL{Y@$8{|>4~jMcaB>n>)GQFAs}dmn4A{9<6>V6{BziMi<6=)Pa#App|#fbB?Wj#pV6HQYjpnSanTXEEtsx+(X;HzxDICs^aEI|E-G7yz2^TOkODoO;fqPeTGG z5S>5&#H%v!3$lsPo~ACXo&&rxzaya;`BJ(NiDEYfMEiw&XzQ1A%oQCDM06a6 z3fA|X>2UX4ciKla8>D3IP#l;d1%p|#CjcW@AOJfvAbRR+IrvD{)j}Ac`~A>eCM`T+ zX66hNk9YbJAQh4d8O<>V=zG{7+Vk#x2;;*%BT&S9o{WDJ`Fx{fenpJ67Q30*cOk9^ zztfu9=frb+EHng6j6^?2P%F5;d;sGqNxsH>KiYO1f`z(Cq0nm`u!e^8oC+hXKy?o^ zDI<$i*w}ghgrA~WZCI(IOKzXu7P|4bznAmpJ!G-$BvD8P!PwE0(ew#VC;75Wp1i3{ zep`iHs$wsxi|R;<9bOCNp|o4GkYo%BMy|^=H{|J7#hYM?xngAo-1qqKgo z=7J4yCrAC#fB_qOi@}T&E^ut62ulwd|Qd+$ZcU zzdxT^!VV{+uFgq}5U6}{oEBMM7D4v9dI1!a;~AB(h$dIY%<75=s% zcdwPe^RZRmI-s|{Ryn0`U44&S2iGqb2y$o_b4}IVtTS5Nbisi^jZsGd$XAWg<(9G# z3fO!#UcP7GDcUh@q`-`SaeXE9|56Mgf!~Y5vtiBHo`&aFvbv*n93n(yHbouP9Kx$7 z9nGEd?r$ts*YD*ZR=%oreK=)=+j(leBbwZ><_?}POM(3vcGdBiGEW;>w8D)$)+S|yKMGRPtnwH_cGZcpjW)dgz90_el0u6om8Msd3`xsQ}SsTP@XLw*;{vYXJjhl_cknnzbU9ZDpR}4eOcce zMEY_W{f*BT$BR3l(6vi0F2}ic`5Y4WG@ztrhq}=I^fl7Vd7I=U9Bi?!?25U67iD!jbXKWy;S#<0+$u_mkSi>-juih7a?TirdM- z!Rz7W8{W^$c}QQcTFBbIF5(KYG}ui#A@R*oZ#ubWs;WC7$i(yOn}M!BeQDJ!x z%uQGoJEdwmf!BZqxbm3y7n(wF={>8+n{=UyEqM@El}7ra9gZdl*wxv2u-w6k3GvfD zW!>P38{(cYh2E$RiH)j=w^mquCv6P>MP>mA|2!Wy{tp0zKzqM*^F3}Z!Q|YtMVrSv zF1{6-QTYb51yg9dhq3$|$0h}>J@8s_`Xf;Ocy(e2Y>MelT)D|z3oMm$sFL6j=Cz+C zeKdCbwq0O4tE@;~eULLwE0UL(jdzg;Ac7R_8+zpC+ZD-= zU^4@?^un~IH-c43dl795;O}UE#UWC`4d8UrUy;1#ytq+VTxugIn=u%{(Np73!te~Z z8HR0#WPC!qsqHuQ&t@wI;PgP=?>JOlcpIQLeX#;eFS@{!Ydpn11WxBl7Rj5eTxUi9 z^m54!y=&~UIKFK#W?};}RfSDpgQU$Od0x*h^A^CZYqL8^|R-t4J9Z~-&LpzlI|eTb-8XX2C!Lb9dSkFuLwf>wpxM+^s z^*pceAgP5;yH=(%oZEWM84i?UJtIScvfg0onQ%Bd6}|7iPIT zBh)LQ2gDr0^Z%JwiB5Ws!D#Jea<)yIVS4s>)3iFfMRn7?M#gY2d7p9^<4&S@vHQ!V zB=j1uH|>_fN6rnt$nt_TM%lEurXzTFV|2q5^LFU;f?Nx$6_|afJjr^QH+<&_@DNoK zuQLF}1N&y;JD*fQ%^{ojP_L8NYw|SASJ*GhoDwjFe>UcB|qoa(a%Dq&=<**uo1py zf^NW|<@FT*q3bE=28?DCl>}M;MdDBjSVMw8VAwik>{bbpnI`qyL>&YiZpmIH0E`%a zpw~pFh=-p*uapcQNKh!UjgVmnDBVXO$YHrsGu0s7DMIV%n}>RJkAX(64n%iTyVbi@v-;Bqx~WNw?XMw;Mk-+o0RI^a02X9)x~StkvtWeV3G#b|di< z+QCJY(Ol8R5&mbGo@Ld_&Y-$6e9>zsMDhc^wXQ4V+#h*Hwv@wnpEu<`ckoFSnq3iNw*HA9Tt*SP4hRkDod7;`oB!U0tYqr8tYr zAfwP_r?o^AgEV$*kn)+}s7wiF?MM`U(O3_F@Suy4=7Tk~qJq=&+g2(sUBIl=TfrrW zo}ReNYVYa9p63`|WU4JK%NmJ)VlfPb}$OffM_))J=lisX`acw35We zH8vb4-k_%_Y^>S*(nv+}--8Ocrz09BLdzxdiYIHj*%a1of!e=iTbP~fA?<|}#|?6T zX92V$^-mK>F_0PwAWI zFMd3H`|3+O)jS5oE6%07_vy$iO>BI%$@Z#m1}R5_UaH^(o2 zI>FqA$2PhKO^rGK%l~2Pqn+d3Z7+`AygPgiYwi2z$NvFbJpp4qPgg|Zq$}V~CX3Fw zxFnbGcAEEjTCnIsAZQ2(%;h5jGbW}C29lisrI^zQMuaf_(;H1gNj$_ch|Ld}{z ze!T_Aevcxon2XA;n2OYai*-I%`KnyQy<;`9y?CM2Wc-b2+K8hW=DY%??>No$E4+nk zc13|To9(S|B(tihYUM1!;m|XVW8JR!ZC2c5w~aRlt*4q#Nr`6Tpi3icxHLY%mmG4h zV&DiIr>b$Aq{KJQmn2{8={qwwB9Gk`D|TtHv&eWdlp7B1lU3D_Iv|`4f-T8yl{9)AQeTsO0t=KSYl zQLoEA;^ChEl2fNiiFJHYqp?gP_8*E>Uj6*C$L-$WPq_NVI?u_@9S8E-Z3|4%$RYgs+SmWF6`lj!L z#VQeL-2L;eQS#!A^Cq3uRih4=_C*(~C;_8EdRGZ2o_3K0VZA{&~ zd+Mj>0|5qsN`Ma4K)3Fh)2a{Q7x!L9PKKRk=S@|tTitmqYvaoNJbIWLn*vwy{s!z3CLLAP zMK+sNHIXO?1B7gHt5eZNF0&F?dgJw;dIs914h;>uJHnq{46hRert40;8TtjJX?4@! zM2-1gSygO`dE5N}(nsR?jN-6VC4ogyeW1Lk%F!^wmlUj6R$`9XM<;w+$m8WLQ0T=t z0NGSFH7lLjqTyqd$i=7Zy~6KreiFcgO9uueEnDIaqg4Dxqu|PCaP(7_u z8zrjIhd1Vxo35Ya6bZI{r+zIPyq7>vsDt~Y2l*V|>uHD|kHA&c!hE2Y^}xfB9Wu?G zi*Q4Zo<>1neZ;HtR!E9l+$4u$&~&E%8Z(MT*P*_c2p9}YPVdTG0X>Bua^$S7@O36} z2R-qQj&#pIZPy7-v7a)oJ^nlmudhAen`88?6IdjAMYL|*LUvE!MCtmIP@?V=*O;;G z-iOyq;h*K}nYzoip^IQogAn{4PWSQ4Q1wdPqN{2Ktih{*+Rhqx=XCZK z4?@7&0xoH6s83j}o%TJ*r{09{eWzak%sm;yK>bQQ*>Sc#@`_FZP!7LUNSaLOlQ z;eLc_q^=;+a%szKiJ+R|W-V#Y`xH^Yejv^A@v?&qS$tlR`Gu$0>6p}=Zpf1%%osw{ zq`7K`l?+D0cmS8SLhpjz3XRcZcx3pxiDeulb`wdXsn|!>ec+u6AECTj-_`2cs%oK@ zx~f(^Dx1&L{TA?LYK?~O?Por;ZKhO%wDYQKcGq%8Er)(^Eq4Gk?B27hCKaBQloP5F z5R~@E`O8zg8u!60<6pnwlEne)Wi$l}s@n#G)<-7G!Z#vGlkIYthf5ia{=g-vf@_!S z24h3T^8Q z5VKd5!$LU^NRMx(of|BW!)Tp~dYyLEC-@H+B(KE|1)*NQIVrj^31 z>h^&@dap+B8C2W#V~?yb9}i4C4V05mwbhD4i6eD&#d#%}BFW6kOiBeOtyN^o8tWem zY2_doqf7A74XYj=9ouF?R?xS|Jo7dXWIm7ZHlg&U0KGG(wtqu@9plyNHu9~wbn?UV zljpBR9xw*XD|KGMg1@M$g}tH6Y+58+2@Q)T6dPN1DCAMT@)sz2mLMJJp=zkWx;xv8 zkJk&mO8&N_r819f&iSx*M6@{MQFV^jN2ubFFgGmQ+m&?~&jk1RZe&tRm@4I_UanR8 zIM+G^!ou29{0EKIEhLX8bxL_-OqRiX0TbbRD(%5(w=+)OU<21l_Uh0L3yAKGr5^n` z%Q3De`fJ!0SAF%+(W4^vs1;PzOg!zwtC@GQ9QVwpDrojQh?4usr;}4_J+57jQCrJ$5Cgyd#$SURvMUURV5wbC%oT>G+@>v%qA>F zGJBm+>M)2a#pxd>V?o*Pg7<&^bI5ETiKEhNjXGC`=t~$llD$@^0-GXhE>(ii0F26v zpVQesiWf>=?Eg72J5=zLIJXjcqw%#PmB?1154}m>@+L36IDL9XiBI;Em=@SHLAMSU zCXyQ61DHaP^4@24Hlv^jGf>j$AGPgIJ(FVsp^Y?=$%lgI+(~qJ!~NXNCsV zkoWhVcKyioDMISdLoKviPohKgIwyb3vv@KExubl{@XPj1XEMY;m1aoEUPNpek9XH8 zvnNivc`9zDj7taVW>E}-XSSE?aZX&1yzgmcw-44foBDi8+E|H4I%lncg8QDhZ!opO zg%dAfjx#(2hdG3B^8oaIP`aRb>xnaYDLC|CjR&|u*@prK79FV#Q&xE0)1$&2T0Ba& zzPQ9rajI9VR#ZVkk6bXc70m3!~KJ1rf{$nzzJVHfe_b*OB_?qKSu-Os>Ta06z6}n+bFfnxajEf=er=Fp-tvbBk!O_n> z7gcfd?^MtX4o3r=0B>mj&wu;x4M;}0a>y^N-eC~rE@giy_i=@4^p@HCqQ>K5*Sn+? zag*Hf6x=7ynoiU`XbD+?yHQxX;X=0w?R2tPzN-pPOVL24e=g&o7Q4VzZmTs*p!k^> zM=SD zv3oRnG~*4U(WLy5L+QwS4e!dMBM0jpzR7mr#?9S9XVO+VHt_}pLmv0L=F)CO3l$|i zGZ__u31M7Za7#y+4AQxr)&cEb(vbx7>!+EUy2+g>fl1e*xmJl;%}pd0v0Y}2&QD!_ zC_U~&#-mJwYh->OJGOZ~3&+N>KAivWy2m8ae_hgzHh`BTRul0CXqa>;lm;#46Q{5= z4iLVn=>9_tEv>G*zCq{u7rzOL|GAFFsMrfbhH{C10?HMdC~u^_4yC@21g79$cvnWx z_v=t&%W6!QcxonPyovtKdKrbutIJ@wCzS36;schQhbJF$7h9Mn7o`%XC?yKX7;dI6 zsluD5P#s_4Wqzbk_-d1uYX5!^+i=q|2t~|+-fIx5jNBFe<2Evq@KEK0Z`sEH~<;mKz8MaA~|U2*%nNxw0e*q{`A zyk?|jy=<+sR#BzkPdSU7>D2avql0aHK(#TT2z%ZmB5A-r#4kET zZIEVkkSRrfg10k`kizN*9eDlPNAKB!Dva5}zgX$h$`|jE(c19GCkX zrW@IV3p|58V z20PE|_O_!0nhm*YOw>ZD^|+Pz_8Uj;P{AQXiraFPZdar@?RBjq^atjps|BidH%Egr z9*d!{?hqN?(Yp9_I?X@uctc16yCDfsu+;8&rvn4{@X^boeY(OcI7B-UyYsrD^e_%P z+a;;h@P2*-oW4|n<-khT3To0QKco=m>WUoSA_>x+S`lC*JNMo@aTNIy(|Fc$S*cAy z#rfEt$2ReF{yYL}ww$MhJsj3d)n>~wL;nD}Xm|N?3pySt_V#Y_mE47)L|~BwZ`Lrj zyx}aA@FuV7Dc^-g_9Nf`j{>@pvk^#YZ-w-HPKUzxc{tnb#spPE4fftUu+B9S%r%rb zu()cCxdTV$D6|_*K8l>>e`Lhb z(Bm&^j_XoW%D7>OaE*=q_^C4#oZ=Ya+)2hj4YP2bODZdBwe{HSd60%y?0{ zY%x#04Nv6r;kLie*VLsQSN7%#yjY6X;D$}9I2+{8Pn#VtkFCe<3z(TtTyueK`jI`v}j12dH>93s)if9<8YH|B5ij|)nAyxa5jY*N>VUzfV8AD9n`bZ>3JcDkpYPqW2 zgS(z9-W>H=)qN*-ukb27Q~P8Joo{~5<&Bddr3249VtduSOAo}J3z9tm1k}$rHw;(- zas%Utb2^X@@dW`MOguB(j^f8oGo4exy=2-=hEbK*vdqZ@Udr~e;w^2RBBM12P<5oUpC*utfdr`wSmb}6^d4jVo zs%!q_s1U3VQ`zBaTObC~1pS>w|u zmWg3X;{p@xHxkH^HFHXih^jTknCV1+m|udDhh-Fg&kR2BuizmWiY7tr60}?K7mN%< zpqM57MkT>D-Y%4^`ZhezSHo*?Dh{Y&YZ5)@0^51r;rA5n%m^3*C>;?OEtObV*JRwKV+b zV?R_J2a+|h^b4PD6zDUI41xWFyEa^ZSJ!Ts=G5s9?SbwayMOW^Rqqai`0O;?|EqTIcs!g^P^gJCuH#3!Qo(ZbJ)li=tcA;qC4 zN(VF%^R@EP)72jV%jYP7sW`q=Ic#pVDUI+cJ1JsTX7lOaj(>kS78N@>v~fxzI)s1V z#5SSq4jNWq7|*Jr*rzZJ*IGzV&b+h|NTB~NcvVKqI_j4}rng@~7x*;kJlc|<){UH# z6Ao{{&h`2vuQo)9sbb-==)T*I38g&;jb>GiQH}Q29yLnk1{%o^EBH(o<)W}1M*=_$ zUi|QmxZSAHb$WH?)zCR4bU>Fb=PYq{TJwgDcgUgF;^p44^4Zzw;+aCv^DuzWrPrNk zOG0PEptPck&aPNT#(u1@OQue52itd`Ii&kYyuf2qc*^y<%2kp?J|yLeHM~7UF49;x zy|YuX0*>1^zEDOhb#-j2=U6q@GecvuTN7L2eU-YM(2Nb!3V9tcn=?qM;YVjQ{)cPa z{OQLNWdJa9W8)&j-6a(Z&;jmj7`b7SK`|39fT05WdWhsraviGTCiAZ(|EJ^u__-cL z(DlydnGKiD@RW8+fBw15-4ikci3~7zUCLfp+QCxXCXAEUOlz2sd(kl&A|!|L_4Fow zz{?+zAF~^V1c&4K?fk-?0`d@7Z{T-~`5fbdD7}85qu5!s&dN-$H-zql9Zowzki8C3 zk0dqrBE03ysjjEg>R=`}E)!2%t@s`-L#}w=;ueB5^4CM~XoIk_-rZI)bBx8oDDq^FFB1W z8jd%>OgWAUbf5FQz-#Glz~;Ir(F7yk)Si~1+S>4e(Ul4&$7@N4o{HPfxz2d_7aC$i z0buxNVg>?;R|5$}xNF-dspj2fyhCbL=T!|@h=9xJzE;drOZPuuCPRItL^lF!QLW4F zmbe!|)6IeHq_TjS-GXhy7-ie?Ewe$c7WeFU8k;()9q$@1PX6=D}$cTZ!$Dw#@$`OO>rkGS-$IwPz3GdsHb&Q!i{^Mz#HCin(IXCLu(XI&$nRLH&edSE2=J=JH0xQ6Z$@{k6z1^VEI4t zde%8ZhSv0hW}Xsn^aakv&)z*auoV_ywUn+)wGkt89Zn-;I~$8-esS62J}%|~(X`?u zdlv8Teq3J1c*ir%Rj#v`)F6LhVJg>z=LYNBYU!R<>+!@#L)Fa@INzzd9nfGC$aQ50 ze(pdMbGYG0s9~t}jo^|SVi?J)uAz>>@ z`()0i>Mb3Sj~>AxW`Pp0TDL@556C5me?3v2PTS;?#09EumdeX;;QvTxkazFgZs0K~ zc4wo^+`a3ZD~S)ZH}7-jRDv_ISmsh^HUnM5nM%w8uahb_tX?z`Z_0p}{Q7IYP@z=` zLs(RXYM1WG*I)n5gx93cYyri@%D`~^0ed67YP5pMf=2K~UDeDUd zmk?1NCpUb)Ywbpm@<7u6YItxF64J*r9NWy}!rTETLArayVBnm$x|x2ID4UeCm#R#c z*sATro&9}JNlz%Hv`TSGNHqq3&zbM|$p)UUK0kzIOuw01p}*^!_;*ik=T6lv%0~`h z^q&EFu<;)f7o}%(S&%VF)+>tM3`S~p7J}=_-tc9Qpo+`6RgC#M2G<;c*?*^+ipQSo z&ab7b6Avys=Pb8L(AS7=_{=6W>%qja97%|5`#NyAr`zZQ7=$C>zF%KNPiy1)wV7t= z(s^eo`_0|+QgavhSD{#|iutuoV!rtVgiheg^~Ty%*V%ici9w^6T6R>i8S-yQ~E=Tg5zlfN#oNr7zslbg7!T zHEEwH`q`F+1|rbRVNq-p8FY_#ugke2M_i&=!0?apYPW$HXE8(*vN>|x*QvIUD~sf) zP4`=h*Qs~lrR zOU%gK)>)ZX>xKftm7$O~R#~D{d-^oj((aDx`)N}CI!~#7a19g3h>N`rqf)`+rk~8& zh^0jVcNB?!lgmaBZm&hDJO}om!2bnMO9KQH00008041#oT|?0j!>bhl06t6r02TlM z0BvP+Z*F3BX>4RKaB^>Fa$#+AE^v9(Ty1mPMw0%nUonwt4`hXcU$P(Wbf;7tTZvU? z$1cmhB&Ahxhu~1e8U%P4Ks2NEf4`@DW`G$0q-n43YEP9|Brwy{)7{hWqZ4uU`q^)% zFVaNiTAg0bRbHiwRF!Aq$yz2$b^17dcyuD37VB-9-YzTg_~E1PP9Hyf{7}4ID)Cb3 zA{TRYr?O(b!emh?xjH&pl*LL+r;EC(OEsN}bhR$ZO2|AfDp{pPu8)q6kB{G|b*VI_ znP+%E7g=$eCSp;P;^IT4%3Nmr_%gR&UYEsfDOWni{G%facv0s`RTPPJse}8>3 zefs?3>3?4S^7Gk|zz@ST`2soJpT(GDHk-?pI)hY|hDb#@T^Zg1>wt^#kMAyDJTsGi=*SB_o2vz;*ZF*;Rkj$5 z)9=L$0&Q@lpEZ!%KmAr!(>qlXw;9Mpccy-}{B4l}6vnt6T2pE;?Dy^V|p;ofydi`6 z*%q~sS*hfFE8geD#-GKfab5`65wVeEZwjH6ER$uh2c2d>;7t6w1Sg=YFlt@mG>|UV z@>XeH7w03bQkkWH0JmT|L!8djQo|6MeSi-54rNk+MY>%VK*RzAp=1_CmK7UpYPJ=x zpS*qUgH#`qtez{D@qIw;ID?;Afe4312qN1&g1Slbd9l%?-$_A^BK1K+*t2-LaHfd*hr^q|zPK8W5e8i9A1DzO*`blOQ z_ADT8A7VEvj?Rldu#w_0FywUIlK|AZJ3=1S2nUIe(cUt$bId3ib zAKvsv0dL(M_UNoX4qoF@CGSa+%^4(hvsMZDmvdeVo0IHck>E>V6dqZv0a%-5inErG zo!&q*DS*JF6kaPiZ2;}rx>#H;h-?!9Cjr9-z}0}0!g155sg8T^0mbcbs+Vde;Yn4+ z6gZFY|F}0EB*v6JK?fK`puc4r_C_llj$s&pUuYIfa1cg@v0zn5!r@-250H5CZWxT& z`b)W;DJB^o0AfN;9&C}-mGan;!zsU0;#VUtdjLZ9By~k5aw29m^l}R8R~xQ0{$~t} zf~D|ZrIKZyCS)$e1}^69z`1T<6Fg9EE7q!j+eWG%Y@4Y>S`KcC^1UYYNUOa}^Phi( z$FH)i&s6tA4j#~=Im{bhmF}h+{Zu841&sqA7tuWk{V~4B*N@TOX&(11iyA=YD~b(PDB(C^c=DAp#PB@rvg86*>)rSFxvj@Db8j-1T$X-)qUz<9ll zZdhw=R0m!n^9b>6%C66r=?hBb6?P1xE$fV3pa$*cedD*0*aa|2{>kuR2(2!@e#)y?qtCMIGBMjLg&y7_DA2DihOkZw?^`Q?dutBGfZUU|os@DJL&@{cx zi_-9VBg>qa<1`9*3Z*wUu^>FMoG2tnSz$Q#@0_F#MC0&Jm@3t>)^M*OEwq3YJ3GQ~ z1#pb}jt>mqXizopA3RTRX`Sr0T8ZoR_5opWN@(d*UC$U=-vU#HA;$lsCeEIiXEPj4 zK0{*?K!ShfUfq^T<)i5Li>qk7TQQ4h^d~)z#GgbYqWEt`nuj7ZK7f&5f?Ww&jedC* zjmNFus%xxSZl`xLtAmKoOEmE0Con|MR8o`@?)HSm*b4-m#O%}+u-tW-cknpsylte@ z_p3{_F0EhP5B*AV0{`RwqqzU3{77%<_xDj3a0#T!m8=Y%r&DJ6bc#4|yjyQ_s8g6BlEc!0WDKe$zdPKAr>a zBAGDX0WIs?Eik2g)5vbOztFxzw)^KkO5~bys}ZPH)e|H0OhrwI1mle zuZy+Hr&=!5$co!wpsGa%@1#0%*au~en5Pm?_k&F;du-!$y9z7a1O5q%vuw*OJ z%|e&OYC40cQI2SoWaL}tcV{BB*JipfNDXB z_^Sv0uO_^6Uem@sDYf?5j3NR88vlJChscPX(Jb=bSG-1Vtes3_LbeWMY z`Jzh&*=(k?5fHclf2nw1ucu~rQ$+`mt{xy-ZR+aI*sP3yCVYjn%su<(@D> zAYv8~v$%0MLR0^PVWKBjKQslpI4kQZ%i?GyKj`!ib|1c5V$mHal zNCi&7iMYsh%?`bxCpq%(t}foZe)H<5%NG~qW9Kg6t5c}mmsh`EP59r(6Y+F=izOlC zRTBGzi#!k!%O@RC?TTfCgk;ap7w#s1@?^c%O|ExqQeU%_54$A!fr*1GUUI&?lEBjg zLouZILzTrT(Q7VZ#0wmVYI_t|?2d6DB%(R7hMlPN@r3E&fmv|@SEZbd%Op6mq|^`A zst`y3u3WFPG~rq-G+=2bkXiEu7@NFRF5_Z%C$1`vs0x8^c~;0dzx4pNq&@KoQ~-DQ znI;%B**y|}6S{Jw?E6(cc0w++V!+;fpE(>;+HKj? zK0PAG00%iS*`_q$FZgN7Oj{kdS+H@tR}erG29%?~38v!ls1>2>K*Vmw;(>^O$}?HX zh(2=}l0Usnld{moqT+8J)~yT9c@N5@w7x56x}+&laGnRd>{f6V+XC@5sb3uKj)EK)~ll2Q8 zUJ&V*6h39xC}hxlvhqZYEW8PqUHU}N5OF&VD$dWvqk+_XH`DKD<{`&)c&NF85~P$T z@+wC`&}I}5p<$908%n9>k*_9PZrzo)+C0)wt~fqu%cM;@8SKGi(+(J<_V>}`+J3wl z(#c(m7FewFb`4GyormE9C4tInCr%#Kt_-#4$ob^1wJUqJr4Mp>^V@H_!rR4j7_&XH z+$~j@z`Zq^S-&n%^+fmCRBJ4|InBA0BrFyxiwh7kiBMx{QcgZD(p$2=@Ln5Ooq`OF zf>Ca30Qj<~Gq;#Q)OmsJ0dK&8sI0*wS|)Fl`^Z?od7;eAI!E+Ie40C?wWo{yy;56B zAS)(e4W%=3RVYP)5BPK*)z#wkU!yVg1TFef$hBYi%G~m?h^cH%Uq<6yfl)gFBV<`= zT6+|od=m}G_=&j8TOLd}UAQ&*5QMD@W zn9i%=oP{ZDvF3Gy9b5)7IH`5!wb^1TJP(Fsr%N+TYVC&sVQ7ZgoIT&MsArz^SbQ%Y zva|$H&ut3llQ{SY3>A%c&{!|iwe<(TBM-n98dcjL;%YbpGWA*8u!|hFl}2#PESK^V z-|pWedq%#{EoJ(?-Ys&^>2*io9@M%tRDd}zIF2xy%N_sF`ws!qgHsn%`)Wv>L9*h9b=j@NxTk0%awIBW}xLsI!T+ zDP9=y|3iLuH42=FH#S&tn{1e3YM9OQ%F8{llk!FU2}$tie}@E*cd(U@(QbA2f5r}% z7;%88p1>!f9jZ7HD4KjA`4Sx#a$RYzX7%j|#%tt9H{Pp7^e(S8IY;i)-J9QGrqH5v z?YDQO)Z*Y=D_f5UvnpXez*RUax2tC++wwY!z0aX=PYWF_@`Dz?B) zjjyJczrDM9BmUlg{rSyn|LZH;DrY6`0CP;WQG?!LT^yDkgK2q30lY?mcuZghjs(*&MC|pTJW~YEf z+Qg-1Tt36@q)kA?gJRM87~M9aS4}2jVoD4)o#D%ekqXMKi$$GfHlw^zjauozZ0i^) zx!*alk(}>F`A~+eicBET^k}vmjXUtDC(q@s{BDbR6D@FqTDsz1CzJTIOR5i)yic)c z2PVsQD1|}V2=PY&!n2=ab2@Ws8C#bH)Oogzk2(NfkQZTC?x!X^xsY7@%Wn~hC_0hA zHfttzpdEu6$DDD>H>F&2OB~QD1$_eo+?MzB*^gjad>f17gsV|pQ%N!r$MJCh5F3P_ zF%d?A_I#nH^5xGlWAU$Zkb<{u-qOH^!$^&tU4wvU5+1C73+L(Ji z;Owb|boxB!pp`?o1q5xUSJ3wecK3?<{OAc*s5FPv5$6U?8QrrCqM)MXCN&yj>%p^S zkD2YXq^mbla1L0)ihHWqB_F9kA`+}pxO+YxG*f(}6lKTAaMNs%=#ZWv=@_7}wYj~$ zX?ypE3*2p4klNEm5rL%s;Arf!n9;vS369t!b1T`YhBEWos0&*4swI=#}q z`1m13?t?k|#QAQ6n{IRVS2*OmJ;;5b6#IjNSBk!--+0xyAz_(^+21%F&127_gRBOZ zkGMW(nv9GvhaS%0>i@hvJE4B|iVqYAZEf>I)1*FeSNW)|rMFE_>$n0i>%88@m!0}> z$Syo9d@#NpG9BAC zh?j@ops(MRr=DFqEPfAD1p5Nuphn%h+v4D{HKoeaDc>z|?Pjp70R_f<&mb@A+odVj z=5r4OU2SM}l5Vc+*&{)dtou6w;#=0Z^go_$PC5uGgvyROv1f~u(+^n zRO152VJ6!KD#Z2WX}dKeM22$WmNPZFcXhNX)>b)iG1|)AfD3VSAhE5I_X!J91yuD& zN9|KkL~8CXdz_GtGP?rqOZdyDbQxssj)vyOTG^ZwyDEA6_77nYw`-fBvx!54hUt*k z6WiKVdA9&$FyzQe;0|rW`@tk%k_k}ww|Abux_WC`oNx}{pYhZwH}(4y$DY47eBeS& zxm)(%mfXqTlBUC-Mq2m}bnH5KJH@ppqh5<($6Guf@EybYlZSZp%v~uq7Z@BzJZ>7Q zgQt*@{{c`-0|XQR000O8C9MiwhiyYwwJiVu<;Va4761SMZDn(BZen$5Y-BHTWp{FE zbY*fbaCz;0{d3zmvf%IjD|pUbk($VKGCy2h`K{+|Cdusc?d)u7;@Q0`ol2oa$Yv~& zT9Wd|`uTr<{ef?QlI&zscdt%06H6q}Xfzs)?na~WBKi2?-TylJxtfc*6GvyIsQYSB ziS{IUvo7XW;^_PQ)xnG8ZL_{_tIMlC`To`G|DK#*iR3S$YwDyFH)7eWS5PnMMX@@7 z!sl02m-J1uOyKXP6D9meRz+Q{H_M_INl_=_uIl=#zLZcdHr28OSc|Hz`l_k(gBJkj zC@IP^DUwCC6bPb7`l7uQJ-uqhs<{#Lu1gkevvOb4Oa9%-@>Pw!`_}_1Jew^x{iYSO zSyHXmP1^%jb<-EvdUwEO@}le2+q1fFn{F-USO9*AcHIj2cbxqA+u6_WX6J8y{nPvN z*-yXy^7j1fufKd8CvUp`ZBv)jpP!LR^yuu}pF}O%qHkLMO-NC*?u!p?g{>G&^2)Rt z#p*{S;yHDB?7n#?)&kp^-;a}7cfY!5mJ+T7^P=d4g8N&2T{pM&--_i%#7pK^V!0M= zr)n%9I;@9Q~~YY*hR&m9uA>5z&*z*7$sYp9crkB00~`|M!RYv$y~D{_UU7e)-eMK>{B&RoY7;COzZ1C!eSi zik2UU=D>A7HSG#Hqxdf?%g{Jb! zbn1Y`;qt(q=WmPUvbX?T{F#IccjSvA!H*c$T4yHJCB;t%vFIr@S6t$KqMavN%v9(6#k z^ysK-Htk%L6_O7}=ak3?SQMLOf12j-80KSfQ?#ck;Qj9GS9pSA=rl#H#SSwFAbtxZ zZ`%7j9c^j%Z`|$;NVKLC@S~t9b((fiSIqi$gAWw~KLL4#YVduHL!QX0NwewKn|^C! zA7v-6tM!p++otUZb_YGfXHRPVBUDHj2n=Nh>u#AeFjyBbEr2y79^K}GQ9(YnkTTCR zZXbz_G_U%lIMqyyoCAw?De)xEA^csgq5c?b2NaZ|s*a-RUWvv+QT zyTCMSRF9xRi!zfG%HW8$EBx0W$Iq-E_zD)8wwRI1DBpWes(R4?*X=>CH`H~P_qSrt zq6u56;bC{0erGt0UrB-=vu11N(x3khy5&duaecdUDqY_q_|%&@z&N)}Tbe~<_Y~4? z?J*1ia86%Vzc`{{SMt zYC$;w@JJCv$CRN)O95-WI5eF0j)h0ktl130ryx*v@|+YpN3xzH$KhQd$B`_u$4X!& zrtS{DGzdMNF=b;j)!5TGbO(!WK}>ZmI%?jC@z{XvbNDaLzzq%B{{%IT%;00WAk*{t zs@yqjvb-}~P|pn4d+r~syVOX5wi2RaX$Ct6%b?-W1Pv8U1%lp@8HMbs?0rkCP&)HJ zHn0?zsNSC_nu95iUW3^z>%?*~Mp4(DxR!v_rSXGhNDcs5@Xvt-W)7OX2D3PE)qc`s zT|+E5Xy3)dxqOY~u{%kzV9An~zYT*FDRj zg%!xTlYp1Otb+nc-Sk)iF^IA|6jQUZ>Zo*xk^KQc6M#EKD~szziwe+Shk>+31&VZN z6{RqO$>GD}A>y*Cz&r*f0qmvahLm}DOt((k*e7R3&4{Xtd^RH|#B4N*5se7ep{8Bb zbCIe3l9>`A^VaU1)iBwsk{1@3OW3tsb2fJJ;W4?`^e7;2suCXE7TuG(=yVU7(j8>e zN_Szjqvw*rcJl$2RcxU6i|amP$Q6x9EGi5Jsyuz^}2|=$<<#%yA!{3=fJe;INaci*@%+b z3xpG(ZOtzYc$`#`=@qnFG3owDL|%#hswp)sbr_zfd zFz(sVt3)1Q9Mp-l%l^Mu`M)QYM;IPPeAME&IaCK+deM8>l|PN+!4X@ z!I=xxZaq6L2Sfs%ctg<$FonMVAnV>X#uI%jBOQ8C_-^He}dVZbz>A>M-bRvCXD zPhB3}Ezp*|v%8%q(sbn3&b7y6okN%=O0$I9wr$(C?XPXywr$(CZQHhO_jJ$nJ=_1e z%UWh`GO9A-Ei|WiPVKnZc3CJnKleqo>C<@X3;c3%ry+K} zKl-<@iIc5e$p~82-O^KH>dNYwrxn!?bfkvE3tdii0b{EPZTL2qrR8Nh$T0Psoe=A4FFz4rM_^Z^>-R#oc(?YLU{HyX_m@ z`WKAtNQu(CWy8c$j4q~jXblK+lqt*v$GMn`w)lB1pZ3^>aL;xImw+diu6{<{hQ*CZ|sB`n3a8=)1Bvv z?`%yKxwjQj74HGOfGh6L4w8y?G~Dd^SUiX8^nG9XG1H8V^V_{&(cJ}ekHs#FZqR=C z0aoYVf{g*<2AhXiRKnoiZ-s|RUSzPzAw)2WNMi0J#>=uO3T|l@kdaw-XS??NxN*upcb$TB!u z097&AbIj&ZZg(#JB(z?)O_AArf#w`l#kVynqu;_xqxQtX?P9e zsG^w4d42*uZPn4}(iV#l9_?d<-R4S0&N1?LQEFv%NiUv>gSEivxcZ2DyKl3V1&q0J zh*R+m_+bY#t}!6A`};xJ#%11fa-Hv%B+6zt{h+*BxeFt6%nw#-A&Be?V|s#_$Pand zUKXxL96R*xU9}eDJ_1VQ@m za1@#f=_xJSMHpBiTr8>X=Ml46v8Mo;aA4NWA-gK;5`GVnn$}-5f{6NYsKF%%sMU^rR9DVc@qSb7>ZTTeI*G#$@@5zkWDW>neum6ZTZF4DckLl$-*lRh)K$b4F|s2i6N=gR{^4gjGISB zppndx8G6vtOZgQ%oNcCrdy*wk*gB%x>+yFySZt)=0P~IEQ|41nGT#6TQd9;i_F244 z90uw~EA1IlZNBIBU<>hf2OD3mcvg0MPQ`!3a`c^3)VjO@#}o$Vd8R6FfI?JEdmLH3q=dU zpfUZ@wA7{E`Iew1Hypn?%zqY)EjN6KxAR6?@qWyCYuCIAeYV9wTNJwpYkyS;Vj+c)XB@f^a<;+v zgLz_ct9#VjYxm*^z;voVsFr9GMaKTyNL}|%Re z-IBpyIZH9oeZ~AC(v)1Ix?9=+v*L4@)S8I2ZN`GrO?IvV`|$X=Z`qnmkbXs$~yxM7&SGv?mYGGAw1 zf&WaY8`Btyio&gVCM$C#s{GRFZQe&a4L_RgB;n_z&%r+Bm>IAoGF<9tPWPV)iO>q! zi7Ag!L+>M8@nXSTb~2pjhwD5tt5`%a8Qd4cj#1_=Zn_{7OSr;!42X6MBD6a(J5vVO z9(omE+<0klXHt`Kh^|N?cLq!o`Qu}ac|2$u39+bpK`I%zXJn^irQ5IN(P5HQDEH}j z<=~5hv1B-!<^H&w{r6ZqRE*8}me4SY7Y3ss@Bt~!O<(j3to6#Q440RQbdo0 ziZ2gpr^7tHd#)uNdQ~rsT>xe*@MTtTJuNBgYZDmhdEfCZ9gldZq`H^Hynu7=4`w1- z=NL*G2r20Cy1?08rX{xvl%OGehYu8wi=66NGPfSf!u5VZgM9=JoXL6UL` zo=_e9COk5J&81R8T7GqERdQ$KgwEFW6Jb)~2KVmKY`;?)$tbeWtr?KM&j4>4PkVFN zm1F^A(`xf@FUG5QG z`D6b;9Rzx5*R?^pc&#u}bW4Pu@N|7nH3^A8>^|W z*9agqheT+E_{p%|7-9q2DT;uq^1~K=I%?x$J+oI(-R-Jsr_|Un-%%7ZjpZoW0&>~1 z`2vNkT(g2JtAJ@hR&(Dk%Mg7JgI4O3nP#&Jw{nvDf{{$i&3z+$qRGIFeoQ{w_nK)S zN{xt>Zz;jNo?bkLvLRBN0&nj@ky}yIS({VeaBhIGvdToy3*=mR0`f*u zg(rzW%~IlsE00cO9v66v5#ETw>D|a`4)WEJ!kwMsek+#6B7gwzza)TXoIp~3b3q#_e}^O_eo~Li^xaa*qSI`WdHh~qS?hHfrk7pK zm7TIj=4}wqptZ@=RmZw48!R`?I%kB^75~D8_=W|I-^CkM#*%;idX3|x**HQ|w^DhsP0% zC=daVRWIz6{pL?9IuX7KDJ)rh<2PJMO0Hvat7ls2B6=E`QnsB!Wkk9LBtLB5PbSE@ z7URKL*je90I0i7@u>+gHKS%^!o5-inK4&0ASRv;_bnYWZS^E|1sJTtx5qS<(?(;gX ztfsJk$5xd=ahoGQd&R0mA=IdS$kw^l0{DDVRO$YC{5(El24p#c@E_>6yXq)ID?-~W z$xH1^pw*d~$O_UT<8|1(zRp>5M6?~Lq(XNmuD#gp1Dkc+o#8RHkH4?!mRBcIe{(Ej z6mK7FYB04DO=j*k;xZ;1#04Ps%x9&G8atFI<%aY{qBIthj!M-a*e0u^o%sz*rCV)k zRgCa%dqpVuHniBz-sqN+4+{5}Mji1erHh`z?>R@e7gEfAP}XmxB@`20OCOZlyN2?m zFhw_}tP#qYRb*xFN&E?QxnznqH*zs2lcgNcF&JcHs8{Zy5k1>(WXuBiF!AP8=j&L- zLYOuR;?evgV4#+r{!{QNW-c?J)sP`)Ip@Tzco-W(iw-;L zO<<XXbaOykEFSK6+81eQ&ZwsS3F`pE?ZZOdz7GUUEo1)FGw{wDGwuK-W8fuGLlp;Y|tsEHmY_eTz=EmoyY(tC*4$Y8G*50=@}<-R_hk2z=g} z{6Z%d#PIn!Zobr(l1UfgP-k&fp|LkrWPf7F4(p@q)$mGxb9nS-9}%>Opb0e<8)4E( zC8kH9MN3gQr#JiFa6q{}Wc$T!>6o0?eEEnS<<57#HCNbxMX@Da_<&~D`O}|7N$JWo zR-hrUYp~FG1QWQ+0(<`Dz#53jsbPj0THaX4A-R4Ir{}L>0oW_z7eCJuw^C4QfYv@a z7hQKGTIC}SC9CAEYzmb$1{M-ax&i^gq5amBx_pR$Iw)~o7l464nBgh)?xEzzS!Jhg8WL&CxJdXGdaYVAK4H%loL1j(0a+bcD)$5=09R&Z6j zGM}_bOuU$m{Wm6EJx~cnP>}kgz>z$0!KPgvWdP>nZBCjYw7AlxCBZc1_N5M4$1ti~ z0fv0af?$(c@?1Q!C%iXBzl@j5qllqX|L~mQ;?^M<4jCgfa=dWjL{H3_9WV@mzJMSZv3Vuns+xQGUuDK$>HHb>L}^lQTvLRxwEHcZGf*Xyu=VNRRZBb4-S*NN zWSd_?{tWJ5Z6j)u(FJysGDoGL(K==*3@uiPE}@Wzi26gBm(La|F!Dv}zigW#ArNJo<697KS!M`q_HoPYj12z3v1NLlH&6X1$-~_0bO={3NK|(H2mM{E3r79Tlcta zkCkG29piJ48+;lAIuZZRM_`I>H0gji`M~}ogTqcE+t*A@O5d=Acxbz?lu{PAuHy1)kOR4qwZG{A5&== zBq#n}B6DrsblqxYa6?XgnLNll`F(8DgVVytb-2fdeNNNI**kG{!0dm=4iLgm+Xp z1^izEjCcRKK!8=p@q8!cGA$56APd5q%>h6_NuAqw%{jdb8H*(WyQ*+wU6?9DW5h8L zc)R<9(c((>GE0)krhPyRczEPUUcf>`D=#aw1x2F|iv#kVg^E@9D7%KOFi2dp$OMs5 zMl-ghNmqwOO;*1!@6N}*uI6i3TSK+-u{k&X{lnx*c~EShFH1l)n-=P!39W7nVme0b zTow5b8Jy%rW6fY^KsGiZlPhS;=D{I>a+6g=1IvP&D;$mDez0D9FY;{RJ&0Td6E)gd z{BoJDYpITT{)?sQx=Y0v)A8l}3gzBQR%WfEk$6=hrNUD@VV;P1Sw`$N__6NSNHu~I z85i=~8U(5cHkvGKUeJW+V*FKDLfCck$T+Z+LrKa#-za!&zCk<4i{}Tu36@xN;202T z1IgeSw)?|4=D^UWf{$yBju9xMJ$Tt4?BVLig@&%cv-MjNzeDbN%pDn&ADb0+^{A zWFrxQ+vW9-7jmvI#`Md|2xgWhIDcMC;Le1`S&D)u`4_w3_3vi|xP1+d&Rs9+7x=HO zIvsufWKRGEm4{cx@kz;C7nz%#ot9DDj)ELn(&)KtI+@YrSUN2LR@jL;_EJ0&*l%BMJ=_aJpVsv3xb%<+hhYRgFAnYqw1hkASstJ0Og$uk0XL&73 zPc_0a?utLo+OI(h$Q_Va5Yy)%MlQ1$T%Iq_M@FNrPVoSXK6{&ti9$;cflc3a4e5Y@ z2VK?J=ZeLQg=~JgD^3UE6Jkrz$(aK=2LP$C#3rapL^;&hZl@|?GK3jPeVjdGs? zn_uThL+qUZQGVS$sP}Fyiv~W>?J+kh5D>N5!~8m# zF_xenCnjNUg9wwhR6BB*od_z5FXmYyt+9|qdETdn9ncwgWVEZ+^Qh!Xm%u{((S zVceVKtpg}A1pf$41eTC;r8Y&lcrvMb5t=l@uy0rs+uZl+Y8>uT@X#TA2gYkyL)GI zg#?D0nRk=xlc8ZsH+@~bdU@YZ!pCDORY{nQB^+(M}X|X`n9&JfvzN*VB6PG-$2@`;h zx7OMgAi2H%lbyLlbj^j#z3$0~yNM&dgVauls5KG9lJ8x~I!@hx5-!rZSeOHx@pZ2}rLfl?KT6R9e`hU7ui z>Mj(KVH@ai8x3UBC2!A6HK$Fpq!`l94Gd=riZEQh*5oKMjv88W6gQkM@TYW~AyZ-E zi?eVPVbkhCnrgVCqQ8V&~2omJ2D4>?}-TM?f76bic z!qz=A7;DCm3A>?StbS?o#fd-;dbUUGU1{Cr+igFZT` zR-RIF3T?u;oCPEdB@ZjzO0_7ezOXk``&gkg8}Tkiv0R0EBP08znc@_?2XV(UeAsyCZ@}*RVKXt1`8jVVlB>Y>9EGTc4sX9;|e7eM3BaRlqq9(DLm0B!A))~rWly; zVD{T@X7OWTU-#d;9Sg2^S$-GtiNz>$4GW_Lk;2y7zJYq&4u074qBD(66E+g)RDCJ< zYy{&Pd4`Fjv*+{rI*8&^Ogl~_Fw`rQIEZT|Jg9(=k5ZS#`HYQPi`T4zr4&msF4?Mo z*L5|S;5A8CgH;?V9~3J~MDi3>aCr(NLWC0u3x%oJ9-QwH&~Mcs8;% zJ=(5AvIubp;O-YJ{Onk94lVCd<;cJmy@>1oPSYXsA##V8x!mIkKZWnOY8pRgNgo+{ zQgl@IVX*a$?N{#0=5JqFlD7W(7FT8MXkq@5V!Tb+YY>V^eN{LUAFr-QjQ$VK5$3<{ zC+d*127|2=&TVp?Ckp14$-|(gZ?l!y&cF6&zJotUb$_MH_WkkbiAhnQI>uwo1+ER4 z{``nJuHj;TLYjVDGoIUVL|moZ0QolM8v?J0 zk$+aQ)6(no@JwzDC?O+Xnk*MxukS-g7G!P}>d0{Z!g6gYnaga8coZ-m9I&T}Crz}! z0KN>2u?dxGmPUHP#{?;sfxz1|;+|G{^3uk7-=qfI#q3pMRE(BCRo0Hr<#<<}@78cN ziD(;+&FCg}Ym%-r1dR27PB9p%`lUq@aW+mZ(MI_rHQylHs5&0LcNf*vDDDEgt8|*P z$#>nZ^Jwxdxk^8UO!HROA*B!j@e>jZmcXrBW^`L3sclpRobG@eh1?1QGKa4~Rp9bb z>$ss@rJTiE`SKvg9=d*ENi2!>ODbbqn^CIN4*xBO_&R5UkFg!pN+2!NKW)=X>5-Z?9Ca zqSIK3DJf`zn!e>Y&}>flUF111JtwP@_U8{pal|_TQyX@E@7?lzTv4pbo+gO?Dqbxd zD}ugz7#*MPhBC6TMWK7Z4&&OYD&%vetjw)(ydC^Xy^!@ABemPA1stJ8%48&$@rsl< z@)@~{ZI)KI%^tl;{4Uzg;GzYSrq{je6p~Xm8wO$?hN{roR{X^3t&>V9Q_#bMJ+S9< z%hVH;rIoK3w3<Bh|W--f$Xi3R1%*SywP5C4{X1_I1B)`ck71m}ng`T(<=hy$)B$ z?DD^GMfZAeQ|mk(gySKS!Us1|d~w(#ndvZmm1>I56PvWJsjdojS8vbx(irozodV8B z7Vglaojo~&m=6y#R)X+So~5R4F1b!%;mpeU^_P2r=IhrjH}DnZ%YG}$1AGE%_$_%! zQiBza8CckyMDXJHAjlINKLPE2EOBC5_BwIw8A$UgWwua%BWE~TZ)LgG%f&x zAR2w>>uV^F>Y(cO*i*J#G*GE{CAsJ;rJ?x%dGTXvtT-F}1La$Cba>Q=>^H1KMNhj- zgu15?*%$476?vx0-$prUkC^xI1@$rQ1}Qmh7lpJ8oa_spQjc2M`Pa55_t~&|)P8rH zo_}*h!()S6DvLs=cVJ&0zCbhh^~Pvf@au@?dJEOt{C6=HfWGjMy`;0gE+nEqp6raRfSX&wV1PN6>DUxWZGcjKb9E^X%A^ zq^u_pp!_*0bXe`zCt|H9V z#do59Fnga|;AP7EI_Xw%-gb*QC?$~1ABxh^*W*k3Bc=DoC?B@kWi~N2#=&;Q)>aki zYc{syl*dvtc4$U|B-D}jWnP+cr|d1&7{8D)eRI7}Q#rT%sJX8`LpMe~%fNURMi)bs z9Xkti_vay`(ZpK!)0NdbbTc{E+sa_ly-m}Kb7a@PtMLwl0{?rf@2%EWl->9=JXq;&jHQul(NBTh!TTYpj0Jgy2S1Z*>SS&PX zf>*eN3kx67&Du2+j6+`Gl=KUP5=Jhs+mKm3=?mb>AoRRIwW;|<2#;# zwqQVa2cG;iJ|(m->2^%{OM-o1mB9BzdGTz25et+9=gpQpy1Y`uvArr#em_*sBp5aqxk_g?+D}tycLQV4{ceN1~Z-O zzKQ>Wl&w*FuWT#L6XWwc=jw@`sLL%SnlXHkh7V@8?W1ZpOPbX9Ugb1*kgA%HmwKbL z-C2*uB!q;9)umm7rj;R-GwnobGu^=8XPs4fVB3sw>5d-?f2kZBW1Yy0bOWkoFR6Hj zO6+I@{Yh0V1AWW$^e=mlY<(-0B zI;qtf(#oM&6pRV7HoP)3`}`}`pn;&26b!BezQa9aYuu?!z{2>Nj}906-4QAz>+{}O zH49_(AE#yfZ)G1D6fPDr78-~r3A0rkM)5_v(}~-?kX-@Hn}7+3ll~!5QJGe>JP`!? zU!%AC2cL^33=ohZDG(6W|8hKB9UN>;&0Nh)T>m3^>}g%u<8q_?Yp5VPCZs1;8EHNt z1>qAK&o^v##4|b3qANrPtIkQr>X;;GJ_Ft2TkIX@+fqm0g4B6V%NnCo53Id6Zkp^C zP+#J`eL33_guGxuo{k=hP^%P~FJmHoXSkSBfqOsRn@8YX`|3-IF&lb!kMIfU+w0SF zZ$rB+Y1D2~+l`dFpv|0x8PXUEJqQTl_W91WJ-F_6YUb~u&({H+R?Ppk* zkgJfZH>@?(rPf1=xpU={-+7*5*a(}Q?l>06upa6()6mz~jh1djre^ev=^gg7a9*dd z@II0zm*jp$t|d<6(z9*^6zE;+R#u(4c(w=Um~!_aj}Nm?#AedN@fsm37Ypb}1M0Qo z?k2vOK)ho&nh(A7*F36Bb-Ii=l znTolp89cY*rW}Ew9$8;Shx6Lz(e@EER}NZ%$TcxVW++GW0)Ce^|U%!sj1Kt)oGS+7}^ z?z&#KG9>wo5?|0I?462L1n1>8>OoDk!ennMW>a{lxsdLfDt$(zME2QxmlXj^iC3sS zd=5Fr9PPwS!oE=s!KW?zA zz*)>DzbJsx5GHrUMHm^5`@fPRxM|aeBPUtr>(lxHv(Tb1Au>@w;$F^kue*q^TiY<1 zqcO|H{=uzq3b5MDOr?Yns*=iQ?)pr}-IXXo83yXj$m^r0S*DCNA88`XViSv^Cwu=Ku!>d3F4DiDuLgy@LJ@K0hS<< zKynIT@O&>uOlw5wDDjM7S5#y;h{o4wpjWSNHV_f_m{8iu(L11#OqN~Ntmsa7crQ;0 z1HN2>E}zM#U#+G&GQ9FE2s5H!Lqeexb>Jo{8z@mmC^Mj&)2lnJp%deo)+P36l7Yu593)*=_zife*$lu6v`&8Gn0WAE%)v=SeF|wLm1(LC zY{X)m;c{i_Wz#X}Ujpn5zLx9yRl184nt<*rkCWl9*yFpcb*|P5*rN0?nwmuaZGk?1 zQLghS5}ZfI|aZy1%A!zlu-O$CxJh9fU#B{P5|5l;B*$lgAN_C_xh zsoQxUtX?PDhPI#w?xPKuQENNsGkVLj!yjVc94OJ)8PBAU3`mBth|&OpKj)3V9l@r5z=fFBkv zqs5s zjD3*|`(E`_!!nyXQAr$g?4qvN8}$F%zSUohQ~>d}u4a+aSoud>b`5{S?_Mu~f++} zb5n{TMj5dasbvA26MW?G@t7(9PbM4;b|y*($mx9NO&rnvQ(F%qp%TB9ISLQ3%fU_0 z4mdOvge0Tf$MI!*d+cMTo;k=VX>iy2HhXp)u3in1T}F1{k=!hq#-cOyO_>n^!&h9O zoPw}kRUu>%6r^s^5IUNoQdWvLkrxGq3iVYs0$E{FZ*ALj3>y>_9GtI@P8&zxBarV(gCh<=n($R#11%mv;c(CAV zWz;+-9SXpZ8pnI}bUDtpmHn&>m6(bMFIQ3a*Qs*yX0XZogMOd!g|Nq*%BU2ZqPiVGv77n*a^V0 z*17p``}FCIuAUQv-c+FRvw5X|;;?f~8qHAD41T%deian(h(|$iER2DmWQWvXO0Q#5 z_g_rAGlA&pq0m| z+}Mw`2qs{xjL%b0)KyGKvH_VgfWeNZkOu;~U~ehN_A{8}C;g4`JNzsV3S`yb)udyM zDf=~Wz;$7Ie(a_|yTv8FatPQM7m5|-4<(1QD(I9oM|v2EOfD2l&!C*)Xw%PUNRY60 ziK@I0B8*hMl_o$`zD-ZOn(`E?rz9;*y!!j{H(^)N^LK3AVfv>blK}JcfRzt#a|@PE z@fMVHa1(lK<=>XSr(>8xoxxe2;CBRs0gZqF0pa|w8Z~lp zWi&9bvbSfShU;VfJo6Gqy-$yX)(CdYyO(W8R8%CR|3K4OEWTj7m3%T+nfnc)mEj!53NQM+9`~k1gN3ePfR+ z{(2LH<-E;n2!?TCF&OU3fT$I1ab4Q4KjKu-z6gFJzv6CaSIlVB4|p$i*KhIIaS8t( z!B>34lU_>rd3}WXuJwX#EZ3CO;^;iZ5dLrT3!!$99&mwx^kaa4@c)1Ft?d5`eL6b- zcOh{8um5-aHYqzC$p;r2L^s>Xtx4Dks&0hk_9_<|4D~x)^`6N+v}V!8Vp7K8B zwJ(1L3_L)I2*9WjCfv*J)zdu5kSy7{apNRPmV8I|`~1EP5`Ob*z0{v48cgy5Qxr#I z;6Cn=>%9LytsYNrk$sW~{}flhe6E=@l-n< z%~o1q+x$u@s&^R(#*4gK1t7PUR+%!sI#Oc74IfYnL>Wk3tQk!aQMp|JUIaQ^xWql4?&r?6B#htn-g zUu!5EuRcA^B1Oe#pVt=NJCJ5tzQTv@S)ZN3C-JaKk)IOYP?X^m(EJKSR5kDSNtJAy zf++`_(1S|{IAH!&+Z<>x4DJgQ{>=V;CY>Et$(XA>k}@kEf2r5+qj%2bw6Hir?-BFI zL&Q7ZbVbpZH@7%yP&jH(@`O-L3L|&&hS@#}P6IHTBn+^2GIBwm*2m0J2 z0uQxkEp5F}!MFAPuru(2+gQ!vgR~a$p^brGp+{%bvMYL>0|S;wnQ^1fv6v>egjM*; z=yJ5(uwX+9TYq#keZ#%@SG>E4ltUnnh!4Bud*Q2L-#WN-?LbJ`*xj|W8)AxFM%Rjc zSY~LA2^DWjX`ab2sn~6%OE9zwRK!?aXS)-o+R{& z9BPAN?m=H5WrkM`PS`+QNBAxyI7_Sm^#pVq{69>s^+s^>BJexto?pK|oL{b~wAu9u zr`LF!2u8Jf0$w`6gci82I!kY3a9^Y2iYq!TaIu-R^sTgXR!f}KJ5%Zn(Tw;{F9J|n z70?cUdRQe9O-#5mQ^Mu~Sp#_pw(Lr=*q3MzaDzCwg{eXj6if+4akZvP)T3}7 zv|$5ly68^u-kG@W{1&US19p-Cm?}I+5NJH&)|bc|bA zCwpy~6)jb;zRJN6PWy-IoCuMW^Um9&J><5q`Vx_J(^F4TiO^;7h2h`17QP;yye73No@rd`v%#^yG;RbS_A-geh>J1i~qAOdg8!! z|FepfL;_*9{nDywuybOm*&qbO)*~_E>v^o5A#Za4CZXDih6-sF3;{Lw<0A*x2z88P zn53n5z4a8;mZQ6zBS`7mf4)M<-jKuw+w>0oN1rnR9Q^R9ja8>IZ|>d=J{*`M@Xsq3 z*J;FiXrT4&vr1e&IpV4b68@rk{&4~Zkj5iWq2yyP2<0M(Dr8Vet#s&sIh&VxUoqrg ztYXTuh(&-o$n69Ci1YPlF0ho${QIz`9rhVfy)26k%yLQ2|d!fM|NfBCDPekc7PFRAG<-& z&>tV^Za8LQH?wGB;7LztxqS`_`dZ74_Er`4LtQNP{DO3Hayo5xxB6?*X-tU0uPSU$ z`B#>?9JFm76LI`Pr^Ry5|BQ7N*J6kXmPHc){@4=wWqOgne!3M?+6?!GxVABuji?e* zCu8xU?<_<~-J;R7HCT4BWjKFO+6+^nTS`kLnw^kR-X%=T*z_jp@r7YGYd)3;ECYu0 z9YSZg3ipzMXC3<~&%&O7aha0QOL$CZt;vB#z3~)l~K1$=ON!4`USOyc1Aezd4OqOPAg=zb${3V8@a+ z-T1|n^am}QQ*nd~(TOLxbyg>)Fbw!|S{Yu0Pv_xX*@?Bf&D9{3mtf2gN)T#XV47f+ zVC+R0Xq4rH32B0Vwkl#N`2>1WQ%oqtF4<0R(dyVUnTjdbSE58H(GHFTp2t=&nc$`s zf}Rp1y;tg!>r3Q!gkw$vnQrMi_OU21ajF0)ySY#Rzf`lUh){$c{1!D9<<%M6ZbEu7 z-2xAcn~)RLQ#sZa+Qzzb{-ZGA*ollzc9SRGy1MRKc@FoUP!gpV#$}r`*7~1MzNs-K zU%ZhwEvtgTV$@M(dB+V)ZSjBB{1l0;b_ixd&d1CV2voH@&@Rf}LvvXgZsFH)PXfvY ztoCLI@g|yLW4!G%hT*w?qC$M7b&W5fz?}9~wKFsaf?y!mIgo50ne@&h`vE(*Ol;(?U2#m+`3_%|s@0s6o-YnpBbt2VKEv7$#r zcs+;z6WFi9Da@d;>b;HA*gmdx%t;3z)ouJJ$){wAQZkPTaL3&O8`<&fhzg@E zrHkY~67m;qB7nF+1!OA8v{n)G(Vj)jQXqY4r^BNaICDPG{=q!VFbYTSm&7xu%2~}gX z9K}jPMqS1m*}M;3|1rtgoBNSs*~A)I9g#|R#4oGLCI7nia7mtok;hsQEsrbNa|{h_E=~XJn{J=g{W5G^`sI}segLQx?b-or05Z8KFxV= z0K>#;ncd0yiW>mU%>6%P|Eh3xh zDd^3pV^YJazaLvOAOv!tS=i^aGEI+Uhb5uAOOngs{1&6EWeGB*UH)(a z_A>rq=X*a;x-!sgH_QtNJyk&wAvw9j-c_zbei`*~WYco35!lX#8Thv}hMi-F*QBgh zl(+_T@Aqju{($*YtrJjHc;2{H#EW6Pnte^6n+xoM3ds9e@3(cZQ4AK~PIs)B?!BR2 zE2HW&D+92Z^Zkgd0YAXUqqVQ?2Sw-w`xbQ56PZ3q+q*zeM(hPE)9^AAStv>Gu~Y;% ziqo)adlPRlGfl4iZ&1~%BOGLI*}Sbc4u(y8CgGM)WxwxJMbBc)!$Xt9nDKDK#r9Bg z8rLJCQ#mzn7$G2)05Dub(bd#*U*l_S$Q+-Fp=;op?im+U`jP1zF7?^gno>YVQX#;8(~Gxmd|WwqL9 zj`zUv7_O|HC1f;1Nhyg!lH>k%7|q&b6u99fiB-ALV%N6Vf*T9GU{7DmL4b>G z<}IgQk3?YdbZOF*DXaEjN<=-B_XNKT#9F^kAl9_}hZ0A^gmjO&Kw22b+OX{zyw&30 zsVpYzl1IwS&AHxrUiE*(Sn$owYKrBocQ33;yz3BZ9DUaRN7p$82@-W%x@_Bamu=g& zZQFKr*|u%lwr$&*{%_2~%$++YBl0azC;@bR;gbW&|t_ao4re>mJny&U+@Tz}t zow#+Sk-L=M6bXKXnFkEA;lU$3t=I(W(W~4{5;U6Na(X~|+T!3~L@`sr;lahQjZ`0t z)-yuD+>X{{<>oZ9$4ZlE_q6mwlnlpyvzlrOMsFs`)W|puOW-kZ=T!mY#lIPO+4k||?cpXBfJk&$(bWeC&QERLmK76!#c_!sz>jswXn~*okSEJ`t^S0W1Q8CzuM(x}o zlN{?FwvZBIateO8+yxqz%NaTAN?L?HltUy~2KyWG@UW)rvf1H!H7Dwy*_znl0Yiz- zF>DH0Xag)!FxAYr_Gcby`O-C(5k~?jqyF}+RO0?`=Z8)iCsgOY9=WV@sW!Z@c6AGs zEL|xnD=TX%Ft3NZ4i}W-VEk*97UdN8IBWF!b=E)@42k9F zx41A$i01dCI0NS9^3Gnz%VeKB>2hx49n89Q{CyzhweJ3SiiAw_Kf}d(`2~nsviBK` z`}gWLkJ5lELn}(F6HDu+yAze=e6#xY%3sFONDPHn5Ocd|t&LzGH;YP~32GDCO!SjO zt109B1cW#CXrV@8#XPk@ayq71h!czW-RR}!G@gV(*RIEK)WsYafWs$7%EwTBU-gUf zJ&RxsEn&`qRw{Br##Z@vUHYa*e*tXWJ(C&hNlOtU@EpP7tVWfX zbZ<&8<@nmXJMpxwW07WJ;#50UqFwuFK3slx#`K3*2^W~PP8=tQA)+vW^u3@4Ln3}X zNh(sJ861p~JkVG4AJImhf~0AxXGSeg;L((-NLzZA*tPy~V+}-t$o`RZkfN_j!zFi% z;0CrRI1Bf(X>)TTt^r&JvtbK4B1ayh2~bE|iKx=9mB@AaHHO-CK#Ja^5JRn2;bqWR zC@Qse+7zNCboux_`lzO|zBASd#=b9%GytLTc|vfTF!a2&1u{H|3`tlt^M-uk{@>k( zTn}YK!?Vuq`T+HlGym;LhzMWlJX|8| zQ|-9u1&6I`_G8U?r1B4HY4rxWtJFCq7u*1dXx3e@EAYHS$c%EGTQApsv{R>-rww;= zgy(LhEfL8&Hpk9k!cVlMHY^O%SX2K$%ndG zeBZ7&kxMR>uB&O(OCZ$P{g=hEWG5j8qwnJ(+pKHRTU0(J?zOS2UTgbY>vQC!xW2mA z061sAOh77HXjylWNmO$#XUzbOqNffc-Eq$&rO4qTa{7;6^>n_qINYn`p648ny>@Cd z>j&jIa=FP$?&9!3Y>iPp7@Iab_aHetd0?1(8)=^_EgRDL;@oNZDt@+r2swq( zy|pE6Q?P*xxGF0G0E?(g4hM!LZLPD{*?^OGcO&iAB2lkbu_hvCkVY26s_maOF;zU? ztk2Q91Dv={3r>R3R`pW9KO|^NN|i5SkZ2tNFqw{lK%= zxWF{75=U;0LmBu=3W!rTb@;${2cblIogXf8WuKy_8aC7oDmsh{L3vrgyy;GLNek;} z$%HQEx?UKcYlG^@D~DcvADfWsex2Y|c3@CAJE@m(tuHE^_=>yUivkL2$dNx|sfV|- zw|mSVjWtzJ_JYlzpnOG${25;_*d$7L?vK^eqrgfZ8BiL9d}PrfE{^1J~JsVv61 z&_G;EBrZgUzgevPsoD&y7!~}F4-XH)WWka?74kwK7br!wp@NbP$Z<;ro=6Gq-Gh+k z54Z?IOC#s9gx3C0$k8IND(mO6^~?hn@PAR+i`QZt;yt zZXLdUvu8^%gy3Hh4Nxs#mY#p zSuOCItB`=_jF1=7^2Yd%*LXCTU;xTK!{9%89YVW9mrG-qmdw^uDp%WunZHz97wgJ* zy(s4lDqr(-RGVo3JcDOr94L0jAm+>-`93{3P2drJQ6AR-9>i`G%uncsEv*`@PGh~>9`Ao`Sk1Re*o zhQOjW&Qv13F_k)R9kGrM3%{^&zgg|x>Sa?2MVK;nTq;iZX-R0yn#$g${h^mO_fkb&B*{@M=j!J%MtaU_Z~o45 zn&fc*r#n)dz131 znv)+Jt2p<7%r9MB$?H(ymx-lwV%*i)cIGasHNaLVyYRVr+~IB#=sJ zP~u~NZ9;ZHG$Ab_l62iVM*~C1aL6q@I`T)inuJ*};oeGJ*lh&5U|H*En9KOLj3Z7e z^#Ge|IaAG2phx~pL|O0RS~{ib4{o`|OZYd&w!Y|Q;EqVhemG+Z>q(wAqxZIIb0O1Y zgOP_)xR)HO2^O(Rsa-!33wp)Hb9z8gMu#*ReZiRgo^Bs)k41$KWxQBoTPI%d2x-38HFITu`nZhJS9j8FERA^EkFoo8yfwN+P|-5p_* zwbuuZCUKkm?sw@t>-IfkX2mYoZ2+&*C$${s?3Ndt4G)QTGVi(_;hLyzyf(Zlk=?^Z zGhEv9&V3!%-3cJ|pRTSm5+LeIbon!F3FY-a>wGvR6EhiCDId>FsTZ=*U)N4OPo|O1 z3#csV>zcQwt+lCF1xR4J(&sLP$T*)vu)bJ1rbv7|DWuzn{jX6idzELgB|%E(dLLJ6 zrSJpzq2YJv$bYmTY_sNE0{zB?0`MlZ**9`3tRaZL;wH+2><~6|FKi( zXy;`AZ*b7U&h|gEgE&@po1zb$zQekY0VuMB3kPuQ5T!tW51V8Y+3S*d2_TqdYN$t0 z${z?-U>|~R?6&AS(|v9oDY-0^n#@S*hqBZEU8gsUv17}bW_-N9>7`B9XNQl`DKT>;;LF%Li_?$RR)C~*NyT95Go9rHJ6t|hwdw^Ix|KX#@YJB zpfOCA8C^5t-IdGbt_?C%TS+X&v=PFIBGObdZFJ{4(h)Ro9y+KLz>ymSno-_kf_u*& zh!Vxkdgq#~5;Fs3$D)dI;woJf7q3iwmB^n3e0@|*a0RsqjmojSBa?QPmbo%o5;_h9 zXW|0(EU+g}?<*=utMW0Gqleh(-12s>ih8c)f#ObWJk(S_A3lrD{G(2p1(H3=5UReQ9AL{5mbI^ilYrH}KMBGhVd$vh$u-R2qw;VYDJV6n7U^ zFg-+Dn?T4V;R_Xdh#t*<7Bd^)o;Dj7Z)f!Um~au_?lD6}=HGxGZYwqE{z~vy`|LvL zk{#QmZYd&r>%U_Lbr!MmyLlwGCYMLvz9-I`qs+?D_Hty7?tTOOt7QoGD~+Rc8_YH3 zm4vju>a-IuJ!Zwhl-IrKNpCaDq*;^vHIVjNnrz59Kku)euD_1XCz;arE(U{@+*6lc z3wzkxnH`{RSY8jix&1sn9N!ZoHtFd|;K>}Gug8zq8!;$6%B<6v_|nEIqs-6{(SUdZ zli$a`zLrdmOa8TiL}fE$m&d`;%II=*q$PF|e3k>&cjOfV@r~$1-y~MX|N6{y*uT|+)5fiZvZ36DIC&MBPnSPC{3NhnvZ0>t-x|V*|m=s zW{f-M9Mtj0f?Dv;0N>=IYrf$hc4{%7-+s~Iy`>WqZ!vh7>qsp1aDcyfe{Hg^3iDdI zKd`Gf3NR!~`Tisi4j4KpZWBDDasOm&@q4aLgubmTlb+S`>{@%U=B2wm$K2s<%==Qy z9C(YssfrXOi=#oXA`Y!RvF^S)-3h4-j`7;9s(?4c?;AIEjU<=BBVDO1^J6v{u2B|A zG)AD`l>!tjI2a{owDw*0(l)B9pi!ScHR(LRnGw zrIeOFWlAZTTjoaj`}RVQbVq&VX{pQ(Kv$W6_HbZ`r`PTCaq@65yng;g5u-i<4+8Yv z2Fa~1AEf!ueq!NV!Y8#8b4PMnlTODTstLQ{7vKuBcL!~C6Ob-w6hW@7=SW425q{6!N3sJYAG}A$<6Em1C(i2 z`4rRB-Inp0^L}WQMQT$3#8z2mw_Meu9X~#R`cRzfPiy1zmavU9 zFuYXJ8ZyiQ9ayb{?4pGb8+uFQ1(yKxkW!{!uc-i0&tm8$kVnFY`0vDrOW5K z=Yd)QxZeq!U&#zhn!^z%sD)OBW z#K0TvBFjdKmY%Kk*Y>hcxtVl=)`Wx5&5OgZEf~iX6)+?7ubsT!GtClGp{yfC(-to~ zOpUpw0K0f%LEnLD0~cIVrC>+#9Ff2hrGSKq(Rzed$Z@|{y2purSD#yY;h%{=yAWehk4A!H`0Yf+? zTfW=|-`NwCqqB11=JP`fI-g}%;ds8aoU1w>0VqUE(tj#oM0iBKH!In0`gf0+a;OyGA%l?TuXpeMpNAF==bS^@ddoi z{gXcFH(3x40+>6*D&iI?T#zwTr4lcp=~1v5_)o_+;$8y6SEkY3w>!ZajdM3 z2LnZO^}>MrQt^$H6o!cjZ;Sh5=W^1Z4_1ps7AK&Q^f^lAOEz%FX({{T63|^N*wNKh z!U}%>Fq2&A)7>&gf)Drc)B{%M6UwzW zz(V{kS8LWe>d+*d@a-nZ88H!2)~SQ}aKZ8j%jT@FteE%`0oSLPDp@oXbceyu>EE@b z6o=K>tBTUe5S9Bn`n%>!)|yoG05I0fjn?9BO*P62Kq71VYFM73*1sl*Sh#BTuMR&yZuW0@3b=r0bd=jIB+6Pi zSNeC+JfJ#qVl;Rnh|5|-Gr5?`o z=*W(b0=i`z(FB5I$04xMEhA?7Jh0*knGTh;a9G0!V2*YdfxwFOgEc-7i~4CA24#k* zwxi!wANM#J8BIMw6c87aU3TGJ{R-LdW1Q=^#f z{{w8|q<**kARtDsOaC!2>1fZ(N95=>)g|}B^YLk+IUTHTC1qa;-2BTixL{~%8Fdyh z@M8QhH#dG)HV3r4R$Gla_;;Cy$ZO*7Wj(aXwUe^4*VZ|)m8O#8zkH3LWEB>MHII;( zvGW%h`koP>jOVs^k>!@Jfz>}0`NoZ`v5IGIgZvXr|lPCCe4I{m3FK-GyQ4ysN|Y>@*J#wp*9An7o&TLlZ@)>fpkrK3HA!(fvl7-O7}H zaR6jdKPrK`p*i%t)QyS7L5cSt&z0ghz#fCM8^n%4D?>9Rb=KwtsQgL%BIJ1@_6eIs zGkJREcW*#$c8)!2^X@HEnC&G*67_O$u;X@8b9o)BghSxg4U!K)f>y||=FkgT>(6em zclz+*@1XUmFwRLpi<+wqUxypRqj(`35lVK>KQ|hSYmb;4NI7`pZlf(j-Vtig*H~AD zj7{b)ADC0EqfB*H2l0W)E`&<4*`=rpoB@#t1Gs20ZCZqU*p$zSlMwTY@U{okDLkNQ zo%%R%Tc~KekYpJTg5KI0NeK@KSV?7xP9xMA3Mqsj9|S(+*pmLfNa!2j&+SS^Q?FC( z^!X5zBcjJeu@C-0Hu~LC9pJT2ozT~)`AU2?XVDt*t-_h*8a@M4M^KtUCH>P=bgXHJ z5N6hT3OwP^UA<@{+ez=M3+;a@MGP=xc#}*q;(NoQY$0A=z9g=D{g7Jmq z1U8jIfJd6VT_9K%1QE41J_7+$|JpI* zWsWTk_&B9APru{EHO2WyKS9+}F?Lxp7?}y=6v7XZzxRMH7PCt^>N{L+R&d>t-NZ+Z zk$Xt7#dKn>wSIGXR5r94xs5{WGG?APKRGLdOqemA*{XOerT}xIBOy2%wkB}k8 z*g9-h51}3P+Bh0(Z2SOQUY!ZmJSEB2g&Q*7Hd%7=Bn0qiR8@S102Zl*ErZJ@ z;%>QCWDaSpH`3xT<{7z|U5mfE z$@x!&Y}h64lk_Y{!Du&EO}XJ{{EIpDJ$*(Ecj8GQHHv(dcNK^&=ItXX3fT`l18icw z;;zAoCO%_Z8LjggMMfAaMUn?2fRb~9fAOH6{vc0!WuXK9muBk$c1R#~&{YGYI($nD5tfTqRt7zJy(M9b9pQJ3GQ zF)eJuw?w<~UC@?5p(SGA)#IuF(@s;^_V)#|rHQv58wHvSGjX{aYHGQ~M)PeTB}4_m z`cPRX3?4Wv&_!d3ha5+Xp^)uWTK5IIOQrzvR^(ELs#!)k%CtMk^kxEPddwF1rv!5v zeG`wKc!%^02I-|nvAkQ^2l8pw$Q_@w-gka(n{366Sx+CK&6)mo}=W^n;RLa>4GayGhUnYF15Zu^x*|EB%kYSzg6a+j}(-|Ph&BR6MjTA3WOPI4f7 z#wYpUQCYfMBYTyKS8n-e=AtL(We~c76N&g=$z$vwGLbD31AX`UGD6zy3KD0re22LzkrIp>NE{=uw5U6o zbMPUR@1NWl3%phyqRK8X8`LWxO=7#Wr`54vDv-_8Mp^V*vnU&!TXJ_hsK($V?)Tus zzula-ce?y2-n-Y+hj^c=-oIamh{S>okq@t%_dI{G$fw58Uuss-d9=-ddE0LdQ}T%b zTo>YcPa9)T++ppRTjlu2E#^&_(d_hxa4>*D(?Z#84e*ZpSGc3n32Dj)1rMubTgu*2 zdeoPVpwH(Ms`R?-FeA1d4Ms3%4qGCveAQCzvz|>X$%NM+q3ebV?nGsl>5FI z2!aV?{g4E1G*>tzh9QJ1plpdfM>s*vrE*bEq?83??N(SwQh51#bc)oIVk>(Mq@4r@BYwF|j?ck{oiPw2wu*+}ZWF z!}qtvqZ7v?z;7sPzkrM)ZEVA9)OO@EVNL-js#FREr;YsH4Qu(nU@|C!upLHPx#)SmoZ%D>8>e94II5xfz=Yob;Z8y+U`Ke{2&fZO zD8_0X(x{l=sTeqm^K0D{Zq=fTVAV1Y!OPjY+Z;?d{Adk+MC zOK6VFfl%72kq>E*mgFbH`xu8FQ7$EeKbOl7D_u^$q9&aDC3rnX`njYXgI}~0uJalD z9fR1F)1cdbEDH|P%!Z$>WbK7DR5V6d27AS`;dFFsx0pSI_HTy&O7>(}m~~>91Was6 zPimuUoI!Bw@t@<8jpv zCqONtT{e7t{P#3oLj0ZIhuuTH!?=<_yk3n3=BUE72PMZA$N^LyUsgZQe||*cOfJ0L zD#nMa^KmD~`hp`GRBBSGj|ejBMlkM#d3_&%KJ-fUIC@AB??z_m1LZgv`Wj%wFp`Ny z`X~)75(if?MafiW2}V)(oCJbyVuuWco_&;_0JOt7^A=oS)QG->pzubP`)`U_M_HfT zp-b39OoAe6=(sR;dHP3>_0}B_Z2*(yFG@5AFzUz|ruc8M=BcquZiy!^3DX!!@_UyE z!k9avUXN#vBJ3k~d-*4OO~G!#-5_Ay6`&KW6294 z<-zm8_hpiczzVd+hJGYTqweKHpk=zqwex zi_z1M`=j&z1W}Ms;^2BIp$qaeki;aKa|?^TcW8_fC-d}#!(4|iS$GKV1BfGVzcHY` z#DDxwN`)Qrc6MZ<9*Ozv856Vh)fHa>53D<`af=G~J#cN(Q%a6_SZ0TM8p^Iir1pT% zD)1YP4rgM&Mc{YR!BPyIF`2_nUZ6X6t!fLjFcvKT;JC+$a($SwZnvZ9aJMTSbN--x zHFcZ=GP&%$rPS3BFAkJBt>> zNio}Q8|+s`L5`OK$c#$!JLBdrmRNF z`8xuJT}o786A4BntfLoEbmcAQ2J(g}hluhRYcH>(Rh=YEsSG?2pf{pVk#Ap}QHv%) zR&d7Q3OJriz`-qj7NKOG3>DO)BI(!U`GBj3DBI(B8vxcY5ixs-`1WVLalMO>3>|NE zbUjN!diLE!;kpNv9No`xc>$P6!WdRx51D7Y{fNwwru(4UbaM^eK^+lpfqf54pI9T5 zF2X!U3d*j6)pPAq1Vq32je9`EF~eP8|AKGV1_`GXMn9d>Q+Nc9<(Roq^vvbsUCorh z0IXRz#Sg3vizczpdGtG3x{)g)<4B+RY8IwC-JqwDW5D1}43hXG@|jUCgYD1*tl6U} z5*EcDKZMZRwCp?6K3qb+(hkp?M8+%lV)Ku@fo8(Wqz<12@b~|t_Cz~VXT$BJ94y2h zM`3KBYq*hek*aeavd3mYlDc8gSmz2Es zu=BP9X=2u>Ld#3TjwV<0HIPc)5{ID;6G)U16Gh}XkI(TUHk)=OPC{R(!3 zeXQZ&_`*fYHU%@*KgXyP*SSwmb@a-_3}EPjDSE1?-cyP@R-DA5@3X71QpG2;1?W=_ zivOEfYg^Y#)ui!~4NyrHQVkzQLE7JlEICTLcfC-a1i_N=?Uv?pncLQ~hEv1_k`;@W zua*eW=2x5Hr3C+CO#lT*n;fPLSfh+TIVoVRwyH@)EIsM)YDKgrX{C=PB8=cH5j}5L z7-WNeVwsw6BNapgf#aFw3g*{m|3Qq8ap_>k?>Hks9SlF&lT3gO^kUzaT5}hYl+gko z1X*K!9|Urp()bBn+%xhrrt)fyaEnYNGArzA$X_yfhnNu_jZd3Urs;CL-w1Vo2@?k&?$)TRTBWccA zo>_WXUM93a*<^Hk(Bf~ZbwjSrv!ix@yI|GDk5rPWN2H!UEKLcw$d+eu8OY{<32_SI zZO-$43SgB#Ctz`RG29L%@UpU9!jW)cRCGwA^zd5&vT+l32_1R+NjLS=D0XbAo513C zD3Jb1T#x>w5L&zFyH(GA?q5~g8Eo1I7#7Iknm37UYEZ6+VHLemCxh)KrKLHK3J$rZ z3nyXVWv)LKG(a^-q+wx$2Du?6T{w!%|m5EK$`4t#U0r*spfX$)v-XU+C z7i*WTj!E2c2$gGrOmTuB(E)2L%Jn)vA%tD=1SdRvx>=uZnkGBpXj`Nr>mxSyX~4Jg z{y|q*t$}V=BY-}tSN=h{%NTJVYBF-RR2|65VCnD4a~12Xe9h>uq0#P^4%BYCmB*Hk zZA%c2R1+dZr>+WlIK_Q3!K6UeFJcIsB8lYNWC8TLk1l*kYG$?&0PSM2%>i1sHb2ndp@FXF=CsUF z+#io+ZmWb?jn`0GowpbrQ$4A|c)>Oh`rg>P7^rkIkLkENl<7ItIULI@8zdjqeH8+8 z9SU(uYFA$zY8%;89_>203vsD{UFqEl3V9Cjg=?qT8TTA;jmo~y?DAS?0}yRU=eB-g zFab=G5L}c<9~74bigl=5iEV{o&mMS@ve?xHY9`hafh!*-Vj3@(AS7GVRJIr?UTf32 zqskYrvnLq64(DtSZFnY1zHt_AJ)t&NuwUCLjs6!gwNo!rL6hK9a=oIQ)m@h zCuY(tZdqS(u|SeGi|TC2s0_}>Jyu@%=EP2U=3i$N9J0qvr=@mHQ5|pVV-Vam7s|3V zy4$iO^qhD$WJ85+c?>Z{?Djd0N58qrtWEyxwFMR*=84x7r}$%iQ>yaetj?oJ53s4# zxk9i*gxM$idKZ5uyH(c$8zZ1n%kCj^cKD0Bd(g}76`qy;MCXDxJ-{m1+bBD0|AXsk z0zoRpG}3HGU5PCYOwvLJQxQ|fYNoQDAIfYMh`EERd?oqC1n6u*TbJ^S|97gss=iEX z-?9xe4te1V)fZhRDla_hkDKV}1WD6DDB=BL{7JH5-Y%^)mD(o;DkWCvaGs$@(q^n_ zbq~W-Q{lU8aX=pC5A2Zax|9VT{5*FP0-A&hO4{c``0T5e{F13SQS;5!pS?IzvMO&I zz+vNdS*=zVf@ODQnZi@=;$bU?uh5d7?qXqZ#ARCa zXE25}E~Cpy^V_CMy25QcbO~0Iy$AbGRcmMyNE6OJA=hqr<{XgDt-bnoqy6rq8;Sun z`-V0hpbn^?Y{wf{%G298=V&X|Sdvyw*5bI>rG|AT8kJK9_$^@?|9z7pBd_ z&T@v6n1Ekkmyu3F`OJCuBE4yIZ;MR+1|Su|iBz~|lX$&~?lV8iJxlSo%|@`>@KJ{% zSj6Ux{xbQ?g2y1$39lY*Y%i(QS{YSgL zXrP-sZLe9+n!!Of>7fh140^Pr0!6L;a?RXbhmU1;NGF^SSQ^5QtjO!rzi)sA-vP;p z(a5xvYW=ecM$7^M^HoQtL0Du78}pDoWXbB*?bhrfNoCxnaP4J1dalg%C2O@Jz~`3|Kzf7HKEd2T1ZLLCd$`W9 zKys?MSAr5spBu^>#$Bc+vmi7aEXvDDheZT{BpDJffD$NMLm-1G(+2b{pew&KdELhJ zq=Y5WE0Q!NiCh>!fhq{EqfVX33y2Hd!0@au+fA358RE&$*itox@zEgfsQ;LG2$ynz z-^f#uv{h^U_3ntI_3P~T`u;dsWrGfsv*^%dVbzdL9^pn;X+GWf8fRuwRM~(mO2b2* z5}sECmXjM>r#ljbDm!`^pSQ1H{;`}U{t|_@CEBc5xJiOY@5?0Hxd8IA53(cs=O)8 z*s`zPMp&D|;}n{@a58A$`NA))cHGWYk)~m$uYRRrrju+o^D5f0YDfb;dH3fSG#bVM z2wi)`*8J82CtEi1lp%DqzQak{-&SWW%ww?&4U)S9{@)8%X-V&VL)Y7hr^6n}JOQUQ zJJ(*S;-plhghUcz$*9l4a33g?`Re7iXM?(+4oEiK7S4IkHk-Z<=u&M#%qcVqJ+df4?B{d5PBKWpt8vc_4&e$52t z;3qG{4|GB?36M*D_pSc{Yzw_-?D6iC3Uo!AkIeOC>X7BhhLIykAu>R#3IzL(^aATb z3?=2v8KT*{QoRv!7W;YPVXDB$FyYBglFf)bz390sbQ3cuK^3t|U0g|S_VJ&l1cKS^ zaDP+1ktQvmmizFP_gG8xnTznV%lik#4l>>C_o-N0gyIH=43Pb_P~l$w417$f)NIka z%+Tv{SLh)~dR$259mX5(5~mJR)!BhWg|w($!Lr$?=znmDw@2kn?+FyEdcQubF3%PSR_ILT1$1juYsCK*q`WhDO8^u8xMsytftXb)APojx$4jcT79G2wN zPqy4x>#8k4+}a8G45tkMU(Oask5zsnJO2jgje3LM^11B6BTY#k9)(3oM~ebqHf_UO zT*rT6pV_2gul}*8BZ9Rj8Em(Z;&&CzboeFx|JIqu8-!J9%Z!Q0s zU~)9ou5zv*_uu`PsDG$ev&OoVnHm*y-1Xn=_}1gy*%TcVy(fRrS#+ZzvL4rufW@X*&+LP;ocWQ z*Y&z4a#u6qOi!~bP3Gup%JU9h|K8^*+Rv-`PCnO4^6btNlOi#5oL>V^|FWBLcy5_r z`2YJj|0l-){DX@8*TVaEw=r?Dvo&+_es*q`_YL_K*8m8JD99aGN-ex^Ir=;Z2q%#){omWdA6}( z{O=5!#`?)5ccolG>7}!8HZG3@9uIqTj>@G?ww_Mwe41t6U-V3E<5_1_`Gk4= zwy{XFuE25G^ulrw_+*Es$l|%XUxNLN5v)u&18klW)~H8rqld!Nmi?hN?D*tclu0y;uo)7Mf~o{`js1_b9Hms8-KMHOltfbJfc*1w@XaLd$xB9LRj9#uffB`aTn!4UuW>zGC_n(>5 z$@gb6-D4Nl_q2fWcz~5iJkLt{s3E8?u>}O0!G^EtOT-6ZvwfDl|Lxq~02Amlj?+X45Vz z)MSXtzb=U~`BK+G^+Tc}xC&`rLVUkDHu&7`add@`M_;*ideXVy*Tapyx!wOFF!DsE z%kz*G9<7@X9D0G?>@F*+PgD$2-dHyu@SWX!VD>lq=C{Jv>avL@#WjOjh zknTx5L6!I=>YA7iOUngS=}$##Fwz5)8w4CVd$z|cV4Z^ zz%5P8=AwB}Cr76Nqo+kZqXK@MoAjIxiXnkVj?E#4+)Y z6L|x1Pu9e-A-;gHK6xyu&Nm*0!N=}S`S$lh@_Ryc-dL(y7m`Mo_w)AOz}!JXNCHBy z)uAh-3y>Sk8|LbDf9!XjUE!{}-(8$vrrws$=JfF9`u=`gedUJl+Ff4gX+VByDcq9k z+e^r0R^e_BLkpulHCHbp_3jjb?$_=x+k9Dk?e(up0%4f+=J;l!$*}tN3E$HK2Xg&M zMQxNND?29&D-Yu+He+`EM7+vdGBEqWBw-{R*At@vAfemCm(Zs3<>vBxIr*nt4ib~4 zmD=rp8h!jcd<}m(y*&r}c7Nvhe&zh}m6;2;si_P(8%k)KLczkHhF-JZgRaBAIAQ1f zutLeyiI779931HvDO_BnnBhIS#%WEb;ELd)3)q{T*#>8HK4uphAkAaD-0Vj}9SBw`(4fsBIRDrGN(_f(r{ zB$AqZKs&4ozdo3aTjh`!1DR6c%@N!^nLaQE_XD&g#vJzHC-rNE)7 z;(}KE_zvb~Bx*7Pk!GuE@hBjLI;RCD7$TlFv0QQ-dk)4bTQ+jYma^7ixnTjv71BPZ zt7cMij)qSj=#Qe`epoTy|Gj^(P~)6k4DB??8tIqFs!;}oaKt+t@nx=i%}IC1K^-Zd z4Jri|v}xY*2SvKepzHKhCn)uCC651cwPds#l})_9S%??efw9y z)L@Pig>zKbK`eTdK9~Lm8%^1m{K^g0<#)1Tkky*(Z|#y*^6sz zY`|ai<| z%H6qdz!}ccZ$p|M3cF8oMDs4feeb3^#^_Uv2;hn0e!VaNF7_PfLPy4I{UDkclDtMj zU~5&4p3Jf+c!+GQB|z?(>KGXqLWK&L-MMYqj&DhQ9b?CK@?B-2cNwZoZL0h>% z_ZW2++{P^4c_LE$Xz->g#FsbZj8RVdhEpVr494z1WPex5T%d0)>XV%6d$C-|70}oF zUOoZ5LL#pkAIJAt{dRXxZa+7^x|dIG!CT+Mepr9!R!{J& zSbyo;rum**-%EQ~e~p{pOZ#np+Iy$@UYp;|DyRA2Z5}1~V0q39^U2X*c`&7gq^*gB8{f0O<{;p%ar0Y3Q(aaQ9~ia zXu9M#nDB_-@6S~~zwZMgmn9j__i33Vx)x8&f!; zaJ0;2OYC+fDtoBkegy}CRMU13GqDcMA>j!h1*(f`%AyFX=Qzs{6Z%8lEq1?6XOmoI zjH7aTp!0wci192dR}~DB9uKgDeM8GK-Q#w|ibI+$_Bm$D#k1yZeLb;dE&gYtn z4&k=tmJHTs9Lq{%v20X|tytGU=q&k9-cHdIxau6E)1xMs%N)QPsizs7>b5=3=$RCL zzFylsci<Qg=CS#HSFqjweGx82}>gdl4%GIR^@5)_Dr48YGppzai*f z7mihQRc4rU=|nd7#sPoGKhtVfLgZ0#4uG`PE@9F7I%AMFs2sJ6OfpgJ3r7Cv>r-Ir z$fj<%1BlLBF95aA6V-yyJ51OXC`eACuJ3Sz0<^AbK=Sl^4ZodT3h_)@#J zdVDgFi6HFo0?uXPv=aj%g4!`Tm>z#(j7J${Td#~3#ExOanGsFqW7m-GAa5Tnm-KV} z`v(M^6MV_(KHW~gCsHr)x3!8UkH^ayrkzD*{#)m2_FH|T-X+q#ndFY=r?9F5i=A}k z&+U1_J}#c*&z{!G<>QedtzpF1OsgO7obFrVeqW>9n+OrRKGc(wqSa%_PEI9}mdZzdzZOX8@OO3Vr7W+BN+R%r7A7Y->Z&kkB!4e6 z`vPj2N#$Ws+Q;DQljOK{>k}MFTLLg#H7_p;jN3M3!c||+})$#y?XZ8oUM2y_+WZjj z04eW1L7%*a)65Ouo6rm=nyJxGFGV%Wbr7Wx6cV9T0$#TBixx*3W>diA8x6S=3`TVr zjFq70m;RM3fTNg<#oCUQB`OkZ=dH!lczu7Zk46|xqa&`N1WyItBn?$MUJ2Dw&B}VD zZJfH&Q-m>xg$2T#D3t!iJysYcW_+ODpAT`Yo?KMs+V8UKsZe`5L$18y6WK}fyqs9+ z6B(HZOlNVs-Nb@jbmXVye*tAcn!l`I#ffJf)jcy@D?Bmoet;I?c)^7G{;++alXo0O zZ_22tfOW1xR7OKK5EEC;&0L&Dd3VH(kYs=&Q%8GwWHmr7w$QzN9*Mq5k&Vnlkvpd> zd+K7dI<2>GZl?L8!>|F3T0@lsUKgSLr*gL7dJ~eDKw`d4(b=P-W~1~96%#FWGqHz> zDu{~gN7Yy-SfA=aee9d;6Q8t;ZDP`;DJ}@wXN14MPC?{;#dRHSi_oWNB5n~E55-{7 zXqsib@7T5A#8pO5&wiYc0EuG#{5rqMB49r}MKfrLILHblCp9ur3=R_1Q8h}!#w|+S zV)y`u@SeCNua2B~Gfnf4&|gHp=rBA0u09p5_$$IdL7gSQWJmEua(R(nj^NLKj4mD> z{rU3Yc>I<4nI8R%exfCAg7&zwKAd-9<^cOK^i#YY2eQvmdolBVGByI3E49CoB=|!J-GegT<_JHkuM^5i_QM=!w?x@P7xh=pDOz5iVQTgumM`B^5L z(ZQW)iOvZJ(qd5X2idlJcK9#EC8JGy`1t6OsIu2>0897iw8spBedqqWJD1U>Ezr?y zJ*9IFOq4uMfAjrIIHFrq27kr$;9)U1Gx9+FweoWttlZL9TBfewPC1-8!-c`Y@=0b6KG z3ZBDJ8m%_arw51}I2sQ320eIiXC5g>t5IB}Tj&VzD3TRcIZlEjG3bW-MGt@GjV=>+ zKEkG~5-bt1P#b8#7k9-3m~VY*)9qR^n0P#~YI~xoC>1^zL0|5F%nd9yNq@$`C41{Q z#X_Ar1K|L+F%ZqUH$!x%t+r`4$-9#uhis<&PxYHX^)VR7@yiTv83emVa*=2t@u6g~ ziC@Q!zK4j7)uM426wmiNNRom?QxCQO1z}hIp-DoOI(1!oVl*G78$&m~53f7a8w=q) z7p@2(+1^z7f~||ntlEIw4E(%6nfjP73oRUs!*kHH{rI|;skhcURRZS8bz)TaQ0kv^ zL&#g#`Ayn(WADCNuE5Dj?I)h@RhZsI4?(D0cj%9ejt)`UV0W9}N-Jj~%~rGQvqx)} zNrdBF9qt;O&29h~OtCEcQi2YYq+-(5%tFq=!GZdIo_>J6JdG?}Ln?11;JC!AE*hD9 z5RXx+Y1A$lcVBp7NsXHxQC|{=KJ&a2S5|c9-N31lPE^&B9(eUlPDw{Fx}~eDstpe7 zFtYlqh9iBRY%sWzMZ;#b4snU6;PZ|KLlvXk7Lq3XYmCmqe)sSM0lOF5Vz350!5>NO zfE8`{QW24tdohH-@(}~EmWk;rOiX0Zn%}hxbJzl5*ivp*GgO`BC2tVwppAyu=2me| zcI7#)h;`LsaE!D-8-rz$&s#D&pODvX&PXZowzFVM3z19n*;_LEdY@P48d8q+(c zta9g!X+umLdD`yQUy4~qri5V(F0ec7%tAk{ZW^jH*aD`$FR$$jRcFnn(4K5ID7z@C z2B8n=5E>2(A;nl7Ac4*t4Q>x=2zn)oN5KCBq5!)MsqHYk;Xkyr}oFK+ySpWy}|b~mT(YKHEf$cp1e^I2RJxJ+qJA^Q@!x|6)k zR*kD(BZ$+yoX$6O&6ZWh@_i_L{N=}^X%!Jb4~Ct8EvMe7LE8xqra(@>DXPS;&=28y z#rx~J&W@U@ZW{+0Y4Di~;WFhNocada_56BMerTj3-li_vLjp!;h#*|cfEmP!Pa8m1 z9#Z~lV#Ti_s!^Nax2j9Yyr>#ix9)a2z`}4A#Rcm!1$q<_h%^W8WHj>Q&)vgQXL=Qm ze4=>duOS-FEzDsyTE>UsO6lM4I=GnTS`)|IZv5%5Kb5iHX|ESvf4gyRD z?Vwgw!@P~u{nLC2JzOp`Y<~ny{i9-f-jS2}gg6N`q^NfSU#RnaCEbOi|Bb8&HMXxn zW)l8wD30_L*m@%yFVm zx`Ek+kz)Z&tkf2f60#mqfYzg|{MC+Zo2#K=~8f1Gc2v3T8)!4mW3(RE467QVSI)kmU30{PYel@^ncCCK3 z^6@+T&rD+%a|}y@GhS;c|VI+b-=?z2Sl=bJ>rgtxv{C%{_)2jPgDSi zNW^zUw?Pryt*CA>3NQZR@OrR$R#u7mbvFMX+~>-wJQ8vK0Nw^C2g*~p=>JfS!Wx)F zU)AaDRJD7GUFf(}q2f%6ud2C@T5#f!GY9s_5)tO051NXE&Cg5FhFkoDCt~Jm{ zwgnYoS4DZ_i5_y%Ii_xYt-Ep2b)ibE2g;2vzhuWg=lY5+O5|NrwTx276b-${1eF2p zNHfo<-jsA3;=m%3!lNb|z2f~NheE?|C13l^@{(*RYitYSs7Nc{g#%0!B(EzZ7bB=f zdXwe_w$C=LSc30yp!LKXU*2dpDE0>v_FCD;>l8*yz2d7NWXZg}Ub|yMnu4n63DzXP z*~D9Mx07$aKYR7OfAaedPDaO&pn``PQ>m`iU7y)cS9r5Kav#|G-X|3N#+MmLS8DRC zz7yzt0K=lTk@0y9D+q4~%Q8oTD#Cdm+bTThqkuB%Gnjzcn=S#$qGb2j8|dP$1HIec zd@u4Ga$4|OFm$S#*t}N6tLDNXOif>C-_6fZA90(Pi|V#vlXT3OE*@Q?W}JBcZOU81 zvf7l3Bzjw9=MBG5>gG_^NKet&?sFEBYPM%|M3vv!fyv{bO5jsQXXq5Mu-I3@f zRdB&E-UnW}xiHKg;li_;vJr=wPY4Yn1xnIfr;nYjN*oTyB)sp|#2>u^a zAK{DD%RrOKX1zd{dgA4DB@Il-xtRtQiN@m1X`Ek`Rh_whE{psMBafd-kk{Eqe&h@Q zq*Y{k64OgI6(@|`afMF_{IJ)pF*Uo*6lDw2P|;}6?h4&G)7`v;-*GEHK?Ps|B!3#? zl_b*7(x>^Qxb^h0j347m7 zK+KoZBKf1-N3G<2hU=O9sujwHLP__pUImShmCsjN1rRY@i{*WGtF^q(Fg=&|3F@=D zZxe0pt?AD2USb~YWVG6pm^^9=P{+h(G=@nsD*;S}LlCymtQvQZ){1cq16KOsgCImoToJu#TdbNh*vt zbhwacY!Vfp?I0~~(`|DwFD|qzuqXCK%pHBGdhyU?QXtNwPd*(LIwm7<&O_#xCLpxdEgz$;{X!h7K93z@%(JSB zimJSFCMQ67LFJ7k4@IpWNf>CdQQ{b8_iK6;q5@PatD}1LB0f42g<`c;dtk!7G+|(g zn74T2BxH%LJc}TIB3Ynn1SyAGr`JqL2C}@i0`z3Qw&&8jMRHZv z{gUIC)j-QMBgPS0Wgfw+3pa+;o);mzz%O^jsM-O*+Jv9c7VrydcZ4KF-6x-;ztANU z?smf82zCmPj;(lebJLv*z5380pY<2QwGGeJ`b4b-QYC}pxX4{A&zFT<@#Yx(L1#`G< zRL~o}3GdQ33Aq%GZZ^ZGXQ(Z{9x;fq15)afDA~{mbNZRAGT`+v2!NP``_&f{cfZOw z(fu`tXumo%Uns}lNmBo$m0-{ZBk%(hBI~O!rU28*F9{o!U$dZgixWF1i_L01!fe1x z&$6e_VR#o4M)mg5rsPH_%M14>pr`TKuc1#;8|D-f2sS#4ggj>}qYynhEO~FliU}kb z6cCl>EQDg!jDchP>Fa>0UD6{mT=MdeMK$Od(Bkzftv`Hj=QW`I5!emxGYnm`V=U5V zN#n?FEMy(WIc=vvR@0^hem5Hw8P3tVIEibKxXiT5v9|3dOQNt=<_yDL#kKGoCOe;q z?HqMjuvU7?Qt%hN>ry4KAMhs^j`a7rq7arB3iOIubw#F2rl)Ld6}E6?}MHM{y}S2-&bg*=2k`wy$S z=mpDl&gG&#dS8cBkMLDpB>|D zrBm4{=FqUPis~w#dqE4u_`7{<4EB>ZXa^+v8qk#3m=)hHa2aYBO1!8(x%8^)YOZsj zcr!4(iwUzU5Zum|hK2L7qT!BRPWF_~rbi+_ac0KvfXNUEf){^@u0fc=04iG`k0acO z=O9S7nTjq!jDe({W`id#*oO0ZA;8+xiAha_|FsHuro-2u5saLZlcPV78B8*FBf0gF z!ycq?4>I_linA|6kU=Ayq1Zf(z86PnsgdV!JIXW{1CUWTEIoV~_N4KkSwj|$4p=x= zwO$uqBg(KG)1KiU2Amzt*b+NdDnC$?2~WCtkH+DLoVDkpbillWGM#^+Dp z|NZRsOSQjF=n5<4tQ4=#-=K0HC{qRm#wx;*Ni>L|X4s5BS>=VfZP=lb@jQz$6x*~x zNX|7ADwT`28W8AYhUFaXHZ&#yKzBz2<#;myt{7t2CyP(-h95DMn%eGOw>uph=^4fWhBq1q3*}S~-<`cu!JSzzZ=b#7g*e{Q zt?*8Y0sx52QKSoG4dDX$TF$br1A*drQg@+MXkvl<1J?adJas?CZj@O3^m_4G7Y?=5wG7Jx*b~28vG=ZaoPewLW}{* z`z$v+mJB#Y`HRPP3>$b4hfB;EcAce*td{!lf?cDG05)1t+JR2T0ilr&2(r9)X{F*) zp^vFJm<15#jIMD7TNf0sw;(9TMKV#bTSXXUi^|^r@Jt8vNtH+KQXVAUKKq-ci{~j@ zql_iZzQx?aZqZEPg+tRm86j(*L~A}ITZIvB_q8lKPS3By98KbeoT^NS3TYSEtbYt~ zriqAE+FwkYZP}(D#U_jK{{x8)mB@bLg zHWJ1zLfgk2P3(Xk`>?iS&*a7Pn&sC1ft#8cByXaa6`O3p(cKbPMJG&Sk=u;>*KF35 z!ysc8ObSd`oLVpv&RRL9Y+7U{h^FE@IMGkaW}r#9u`(6V#K792UiXse?yz$mRAxk< z6D1wXV}T;?etSq@I)`6|+Ae`4HktQQS-;oNZZ;yrc!YvdiGj}}kSe3&W8+bWCS!LXJ$nZB zJK-4P>5P|^hZ%#jIXK!WiVz{=hK1I!!5oGE8T};x9Y9{tF51NnNwNqO!O(O`$g3N+s#0R>bBnniJkrSElTRvZCcrNvRL z>KhN-NHLIw1w<}SGms)~sy4MKXGOCr;uDX9cKhp#N0**xZjOR2TkNUvc14~V?3@UL ziMzagVd^NK1A)d^IH1F0DCg^8^d3pI6bX08mkSk0x;M7(FzorWtf+1ihahQ$^41)x z9o=ESKOymZkzCbf3LbA^1&p|ZW8sj-b@Vk+e}8r=^KJZ1SJ6_fft*^ka@AdA*DuD z*k)F2kGPSAF&5d{j<^8A-!K>v03Mo#wWIjxIbOOQKlkq3@Pm*}sg6ps#TU9i!N(E1 zb@!yqf)0?LyrPTQ8)I4Z*pb8@4aH$`rZwp`k`A$;=^3~B`4r$-5p|w1w?(fdOeUhd86pl4M#>B%3iRE!0_9K` zQ3DXV9boPe9r>5fsWs;Qg&%At36Hj%ZBFAifxkO81t80r}g3p%@7um zI2%CoRe{ld$k3AaAzt#0@RooRbYk#nF)BlRvvKPv#_Fc!6nVfBZZLL!RZ15MT!)y0 z3r@eNYHpQlL>6|Tc)D0bM`gt%aIC&Pd?;Ox@g7e(PG~m^zw-|bvDsM^?g?V-bihn% ze*F5)t2Zy{w&iG@ud}~I8%$!(t}TOlVQYoR*~O*p!${eGmH`IqyLX>O7(>--m+$R=9zmuQh3svzZBqF$K3WmKrM%Kyc&n|5DXn#FtbGnfU* z_SV*Z4H}EA&!aJ)7z3b0&jWf+<$AkJB_<>x?%7#RHch>xuNJ@es1G(fb&Uz%r(#l+ z2HYfGk3DP4Oru((R1R{od&%GJz*SMr(jwSC#V~6!y`iqN`be~_x9cI@)s6J`_L<*P z+NO~`k~8_UZ(luq`TqFXk3Z5EIHI(33dBb~5Ev}RfX27J|B#t^X)MO3sx;nm=jZK1 zqzX0<-7st!w$12Zc#(JKX}ls2Xtshk*QhDEi<>5tG*1t*Wr{KxQxpFK_W&9`M$<7MgB&DKl%U}=M}(VY*a=R0BpzsQ z6!=?{O-1%`UF7o|FC;k450*9?#U4|16tN4{*{ZrB7e?v#$&<#WH}e8E9np36zJ($g zoGF1IN6-KK=*b_V6FI+FBnI#?pK;;qne9(G;zY(a;`qw0wK^jBhl}WXQRFOLVVP&z-DloqHz*v~sjMW12xgXN<|mWKLOQ z&8e`8=n7dm*ls1C-+u%<@)BFE1~TkA9dQ$~cXuQkAZv*DSZJ@Bmy}46RzF5ZhQn%B z&H=+o^olYT6U(bD@r)QC9B&Gh?oC=RQN~=+IdeI^eslif1XnVhgx12GFLYiq`{Fj- ztg|^KXdy}O7FQ*#DY3T6;dYiS(X%dZWv6h_Sxb9KV$zx}i@3Ix097$UY~BkdCGJPz zT|$AwLRVnwlFF^=AGz@|nM{3y7iriEhPM_PQ7F6qaD}FQxoh7gKOym9J+Wu?g&5#- z`EVP)X)-Ac{iLAnm_U(pqknzwl6!Z{VdfBO7h9w41|2qtsu^8_q5*cxMRMS_kh{p7 z)J=!c|J{l`K=UCTD#T3!M3H)d!kcTlc1K2w3rttVZN01dz0z)Xp$lEsERn_?7n&!= z>87n#=~X_DMY4x)DmbVU*LiuKHL#~XMK@86u2GC(#L`6*kKLH7KpaO*nGxCnu^xd6 zW1vU%;DRJ3R?fs8Sp%rl>dK#e}?T zuIhB*TYlIOErQXhRI?jW)GBZ=c4RGcx}@9z5;;?mQZ}xNPGRK{^sq#J1FJ=V44bRS zk2!C6FMW!thtVi6+mZ8%tW%zhqp#qfPLSdr=La;%po^RT!Bo({o|o~Rg@8wwpOQNV zB~}bV+PX05H?o3F4q_zVp{T2CzJkqf9DA?u_OfJH4tFNQL{fgvuTy1gXCsE1_p`?PIh@ zpo}88D|Fz!jb&(y7Q2ssT}P5PTJ2$nDOej1AS8sd#s0fZiJAslBtZIXst;U&PAS3` z9~ZPVt{2LbgFDs^smtu~rvsNg^KD>f&}>wl2u}bvWsn#NROmT7I2AgSFGR(QfONsV zi;w-+1m&+g5C&Rk~FstTpTm^OWNZ!akAD(bZ{zx-`5Fz-9s80dR&N zh3JoLoT)(diHtI4TN;3y5r9E0YqBR;h|?+uR=dSK9O4*SkpU~4!y+!S8o8?IZUCU5 z64D3KeTc;3LI!cu+7F3LVSEyRMq^r3S%bGs(wJ7DN_wWykhRmkP^g6F^tx?f9UdPq zs(F)?H~At@@e=WPzFDO7&yggeqXqCF_=D{q=NR!Pxo%g*p|3oWAH?y1V;oa*h*oi% zV6Q%M%XVqpyL^`~q1(en1Ved6U6IQju=lH3`sGoQavhB+`A6N{1iN|g?+o~#PV|2UPY@6w4 zbXQilyDXm$>b5K#>iWy9l{p$Bj>X3;AMK!6mrq)EOPgK~Q+al0h@mUJxpCt3&1u69 zGmVV;GoQ>WPbU6~he0hIqCwePvl|vA;hwuj=|Q^Dy0{m3v4g&Qrx>zY zOF;0fikl3>G3E1C*vQEuLOw@7q1RRWE$RsaVtTMSBp6H%4^6Don*3dhYBq|$N&v_1 zzI({NRVtV%6lp?D;9*0;vqQoWKFFMX92zB8F&5F^vQ16#!A>G!0~LK$q)q!(RLwd6 z@tizc<71TXRYh2g!UmeACqBm2DZ+syX)|3{4Ki`%{&~!&1Ee{8aQfIZTtE~}BW6Fe z^m{O5AV8G%ZLstNd`%7>`_RAeQc(+_8O@7k_l6<&1Krt!gM)8T5zk3H@LCC&(eqcY ztV|YZbd}@DQ*s|$2DlZ<_w)*hp(TTz8WSSU8a8TD5mc4R47$Ea_4$|PNJLn{wiAu3 z$8^EQfrHTCg1RQmL+CTRbu15-`-Eh-}t}&hT85*GZCi z)p;k4vC(&EE%ChoO`C$LP!W!>p4T^x}IXI|PIG5>UuQ4!`Wt5NkM}-WYBfC z!ka-lIHE)SEEARhlmlF|3<}AY1UU#G6R#Obhjb#Cbq7{FE+b9?TitQfthNEJSTOuNvA(LxaM!y6%Ioh?uBqU>i{xgeyJ3SImA^4Dspp&YgyP=A}Wjfk<&h{ z3Vaa!(QdpJwy7dP9p~MZ??{t;or&xnDkv;QGNLv(HL%@?%2m*+_}f9t3xkN#fol^# z71G@4lejNdj2Cvo_zo+DlMm<> z@pJE#llU2!ZZ#ZoP2lz3DOb7o*7?TBBo&h4FMLaq&OO59(j7!$W^YwtmzZ(leRGpj z3?AzaR?4$Ipky+uHenN$Kvus%=`^(Hy4IK}Y<5Baj8wKm%=WsI+2UtFGHCn=1PwdB zb>Q>AW;+*6)49OPUWT9l`RwvDNAF@Fb>xX)KXxUWmP6Z9L`nz50q#(fIeDkkPyjw} z7)cs~bttvv&V6DW z22(7@-=qr}A1dc?|H^JX9=kFZI!3&DK7Doe&AX@X{yD{zdAK^AQt8;T9wa+6K-re; z(1rd>nhV}wF#RT7deKSXBd^`BK!n`P=*FFhp5~Ol93a&~_!bHOruygZ5`wQ*?h17{MqKY_W5#W?CTbiZq6A?YoQa~kX z@l&gL*VQC-;X>vMUrgE!X7zE|wqmKWKOYWn=4tHgi=1;d@s5F-zHltp?TKuGSEv1;Y z@k~(QZBP+9SOYm&QEH})V$r5k;Ykg3Xi2-}A<;>B=l`_a>XXK#GlzHgwVJ+B2w0g? z{3_G9DJwZv6E6Q7$ zoDCd@-Alx{>^uTeB=17po`<5|PIiEnX#qYey6*!bBjoY(Kv*DPW~=3t=B_B@^~gzd z#t)vuDh;f6u4D#Y*Y{ZqYdBdhLP;|5@(KhSK z2nn!AXt@gU#Na32q)ldawKBeLq*TuKPH_l>!B3mQD9N`iX4ztjGj+;jA3OGqt3CvW zEr-cYN_Uiq5rC``!E7*p)*8K00>_-rRVJ4V!}HHlU1TrFx~tk3!2Ce0%g@S)Gc?&f z7sf7xLLO$hz@E?<&&cB$klW%o#;0hzgvLyqBkcAXokP;16&ZAe*NCud5&^c1y=$45 zlw#fqK%(v<6Em}-xMeUL3$RNgvb6Ozn9*^xScVV9;zLT6rAL5E!a!pL`IVuvdTy8; zTJw!jLi;BJpw`9@_N+>#lEJ#DG!ZGDgwx%zs%B>y<@3RHHvcf47Zv>5e8_G`_Wgxv zf8rRJD8Ys4CNGzjrWsF4X-x6np)|8Oc!7|h^gH~1CZYD{M%uATcJ9hM>f1QkYpb@? zzwTVkvBCgt0l}QG=H9Y9aj*hN`GfTl&z*M`I4KS+r9v@{CDjqmo>npj7TL&XjTaa+ z0he)|F@d)%Yba4bUG0aukX^d_6N-X*0E z>VDT>FxoF^O*L!B{i+CRZq_GbKlm+!w0cuDAX(4SrORz_WtqZ@MU)PMqaq%t(W zD&r$ls%$zjY53)fNcvnw?EwQqTbJ4G4wT{^^pWx~O2@!a9e@4i`}4QopO1c8JRE-= z+e|s_3TC|`C;&}(Uf3S5_~B?>tNv&u5rIU?^SmmXs zh;AY;bf#(9Ob$o#H?3K>v@kxlsJYeNSPnT$^Ig;5gXnXJQ5G|LpSZVqR*;RPK)=Ih zy6t^JN@B}Ws;4dQj4qb&9u__I)jp%tt>dZ-j5IV_YUBQ+wc7MXEZy#s&RhD97+AwP zG*oTxW7cp<*$=bi8`y=}E-MsObzrti5)r6dI#^6~lVij(;1N1~qLt#^eS8u!TBPv! zXR8(8=-}S;R@{)%9%XFY(lVn*Bv_i+$7Qp9IU(Kjg~CmJvAtLr;vtM>540g(~jU9Z1W&%Zl&?VdsBiCS<8k#KAd~8n&Zu=%H zJRfpkp*S7yHYN8z`rloPpi6_NzH<#}+Ai}vhzr$fsWLzaJ2KQFHh&Q@|KV&z&X)?9 zMGX`}j8Z^EX15HxtapS0b{(c`8$PM7BALWf9$jg094JKjPbb0QU`D(hM3g&3)J-}e zPud+ur#Xt21AJ%lF6+pp&@TlJY^Ije3@%SnW$wAS+{+bMHQ#vv9MI5yH_vD)cNdQG|dl5QO4GQ{vCI-qPB99UN5E0Nh)4!Uuu45c?ET6m2ZD|9zM%;!r% z$_Lm+d;S|@4a#sUY$&rAJ?~H_8hT1UBr&BdeR#^R+IGhX_)xsU6JBjP7uf+VQuvz;n} z?eUG*0J%BHNf`%wnb;3*Q0_!t8)9XWC%dipNTq`m0OzZ{UZLB@m^C|>lgQylA)QHJ zrCDxdy9Fn+ufC>q2R}4XncLj+^Kbm@&n_lA9-@o2`@4@1P3M$%$}k~*Y?)ey5}abt zx*wmR4TwlMO6jdIlR#P|(NihiFRSW!mev$J(Ws4*%vFU9^#8geciiV^?>-&N!ZYF> zO^bof+sdeB78c*n|M~WXs%hp%ct(-4Fm1(cMxG|frj3BM!T^9SajJTi)n?Xn>2<+5 zTU*$LFt^r*Vp6g@F9%M+Ai0cJVd!eWpnEY+p<><@QG!xTglhB!K((-ML=^pDz<4Eg zCftu%250{aV-A-Y94ngCJgUgeIb`iBKK2Z?abvB}o*=R5m}9|v)pyls&3^_bUI(}s@6Hd ze1U^oIJU22;Ti&h@A0u{}n9kx@)Ca127d&=m!XNrtM6oR5ImVNZ-N$`{a(u&dOBzUcTf{?uh!|MBP*R+zg%@tIo}bYJnxE4 zj#c$UWH)KybWUEA@0@^sg`Y7TA>#*-`;*m(%sX%040rqpo!qQ6$40g{bp+#mUG|Hk zKa-0FXTigwI!(F(i!#l8mu_b=5(t*OX_R~ea4|%pQWVNFVD%qb705!P8L!ur(o{Kq zD2kbE1IFO6u1*dmH*bRncynRTuHU&nq1UQwb$BliNHwB8nH<-%Y`TNq7F-UMk-;4` zpsmVF0jLAVa_e;wd>FkzBVn0SUaTP6DzzlATAb{l-+n1TK>Y#{7BRHYLz@Uw-y|vb z&!fQ|wNt&f<4ywvma}(JGfzP@t=ZTP#p%_$#r4DvRhf`t$^5VxT*4vy%;YgcPhgjf`E@hA|lBuKbBHNsYI`wsN&3b??k zn%yVAkVYk_Bt%!Seh$$;>VBETONMp!<99FUNWd336yO%)(Jp`;bE>=i3gj}%+ZgO` z>_C(mC*l0W>7L0TFkf<9$~s$Sbfyy~qA=KBFbt+l7QJga!dD|uEdKiaoA2K}dog|Y z=FR!nln^rSMq94a*xU6cCjSjiTKqK~@b+)VtTr)Nbw?}DzJK@b#q0B_=pGy{_i8Ov z-LX9~Mmn*sz3Ae|(}1X+J%91`+p||Mdb{(PS0SP;tx_Ul2|A{kpH*{3rdjsY<(&Y7 z%JARSY%!_^A+pOxvW?i0gZxkYA=}(tB_qMmE?vz+~o)GjeadmbWz2Z#ol)(js9*~jQCIRE)ft-Jf5sJNf1Cx;i zvkE;GYaqJIKjHeUmeHm}j$hJ#0&5Tch0WGYqy?glI=Gs$+xyL|Y4dg?q_;Zbn;p`b zWSEPr&Ez$Ys>MEGfMc~+o7d9KR90-JXumjO!BT4B4Id7U@~D6`sw-3Bbw)8L_`97L zMBPskam`TqC=`6eUMTS}?w~L?6t&LOvC?wEmoMM9@NR`s8I?hQLJHgsOkmBCbaIfc zOizcL5=qaBY$dL}e%(}*`W25uO_4U&fEf+Xs44YL9L63^GBBM&Ue61%?zqm1f?LP* za~Sl{)J+{4ajAJkt^$hPzO?jRT8d>-PZ?b#{p+^cV9E(j%==O5Oe6O0H}%Wpr=kQ* z)LAp$u_J)Sb3?!EsKPNZJ&7j`iytg;QLt{<6y_&CEh|!Y?Rb6%Cu?=0+g> z)xvYXcNhXibJ0A?1D&J&Z15 z-Ew^C`oC^Ui8g@LZ)-=1qep|i65l#a{}fZ> zpMHuXjOmq$;0qYei;^@(clG>}<$fq)Fp1DEQ*_1FG-gBv&|RXu&r?jK6VUAzRrO&* zZ!zl`JgK)MKbA6Vl>{ZWn1==&JYetd>aONXCf<#oQB-CUxs2H!swR6OlNE zeUg(JXwCF0P?sa#RyUx-DCb->I z4hFcp5Z(%G`HFZ)Ns2DAvsANPkz~EIpwQ*ZwZg2o2QEnsA+XZ+s+SuZ)^>q?-z#~K zzz>mJ_D&2ABV>@%90-$ducfzT3Of! zH~^-_7l^Nhrp0b}HGdnSrh1~PDKPGEsG=EPrfFqlHLIM|S<^$Jq%mpW>7*{Mo9UQi z)mqx>X_d>5jjTmeA~z>pl{G!@?bRWVjWug9^-xLMDkgq67>Z;eOEjA$Qa04O!`$G2 z$z(l`KXt{5i$vfhU)kFMvZ)9rX7}s?8Xl3L#^H!@`o?HcWH1~~{`lWY2aM!&+2V+t zp<@qR7cegFKx?*5o2}Y(Jsgg%qF$4Ty2xf55KzBmsU1~+$P6Wa$7or2sG$gUq7`o+ z(MeRT5J=AEJTQ2p&H?54kh5NOH0}t2CNDM;Rz$02huY#DM&H5uSJ~4^<&3Oi!FtxG zMV>ao!U!K~dn0k7FK|KTL{U1cZfx*2{=hUSnY_fq=(_mJv3v2&7&RbiFL?J@ zfebJMj0+KqIk}V9oOhohW$=X$Cv+bHPY;X{9H6~)jf!TNbuO3uRCZm29=yfdrFe-G z9UE>W!s5fe2T{wOuok!VhU@%P9&~_@wfUDgcS#@|HA;DTazFtYJT-6!8@_wRtUId| z6Q79KevHtG8_#xPPoDKS+!el~Kmfb>v!n$H{)s;BXC~M#iQaY#--LO?3vXU}tynOwA+z2qF zBcZ#+Eczp(V3Usc1oHjQ z0jlXxKb=I+=;($0d5>c~COG^xYv)OHodX5KJTMo|92mbh%6wO20fEnIl^t{4VkYQ2>y0w+eRXCKck5iO=vgVjl z*!VYPOPimH7*iw&b!78MuPhk^eghjthCdQAco%E5r;3kHO8O7mo`}e|I%2!Z;G%-! zE)qU{bsMip=MG1kXiU5c0)Rf0CO-CY%JX)deJa~jB!`XrI?Wr2YG{nf>wX`|dZubN zV>NebPEn%5>+SeCwfVps&9J@oK7~Up3Xq!8niMJUnX3>!val3=h8R0!Ga`w z;kw{((@4@3<_Kv)te3u=GE~G>nOS52g+O}0!$rD-^)_0){hk$wM3)r%n)_vgYuzBo z-JlnVP)RB*g!1;_$6Kc2L}O(+vQr;{Jhv%A8Bcn!MIk<0;EVHX*aVtmv}QsvR*2ll ztUwSJH;#h)c^SHFfH*wdL`IVF@l7V#0#QE|9F})jp6daKw_LFFZ*?A3b%Z8Mp{j~T zi=s%<;{+g&{jJKtsZf#8c#J$3-3U_k;LPKS(?M4-zyD`!WcK{kE4Emd=N7X0k=~>F z^s#$^9vTS9+{sanfDOvkWMlz?*btblI-*}yvUAO*hMkbkO$2Hu9Ow4nhk3BR+Aur8D*8cX|*DLx9$I??(UO7x4nFIj@}Z8TH!ik6)(Xj(nqYp0`l8DbgX zuD1ur?ncq%^Y%dbA>m^J=llsLkE2-q_O^8os5T3&gs%TID5w~`?p~-dqtxG{`j^}j zFf+;Wr0rah~D z={2=$J-IF%L!(QQBud&eR7;rBlMLK~%CL!Wvt;Uo5+QAuP|K5=(9*?|*qFBodoI9# zp|ipv+2`M;^`bdK*Y>o{XL*sgTj5#4N|L;#C@Ii2%(G*f>1#OYO_eV&{CL)KW*5N9 zTpE=(LYG>h<9Bw17ViVqY!ynAKtTBoYHj4wWJM%e#4B@da)a2}S(SlIuOZ_y~SOx8%Q7S^>= zw41EFLMxG_9OnP`|NOtZnDFUo^q52Dh41Itdx+|#ZGMn#BU`1a4N3fNVMl0Wj8kaedYOkaBckjcS%Ft$K1~l zT9m*N5LSk-jQ_bp%0BCL*Gm2G5qS2b(BEQ3{-&$(f3W;Sg)JMJu*v5@Q<_C$*K_hX zI4@N-9n{k;5{@7a1G(~7jl3{VI2obqoF-M*1Q?BvHgzU1bfhap-r)*?pFM1s)NdZe zNh~uH`sjA-ga_goq`AJ3Gp4Dds>3Y>GHZwzW);2^55ao$B2 z>^XI;C8R;bb;yFl*J4_HHXOr%&6O2MMS8;bDJ0y9D!pM5RKLY6^%Rn0itqH4ee$ng zyZ6O#5FydiEffxo)pp|j#B+bcXi2~X#0*L1>JhNiMv!`XBcC-L~==&>ImNUg5bumwxAJ#2+R zu~ou$vfaXyYTqGkMjMi~gfWR?co;_yacJSi(tiQ7yQ$s=WJvO5Vn+`T4VMx&Dw`j1 zhe(R*U{wn|{#6df%Q(4{P^pIjYX(@Ju+~EBcBg04?qI+EI%?WrK2MQUK|Y)T3n1+} z8?Fc8aIXbG?Ne>eJ+CdlHjC3MlPjQLO%phXJ2BEGq8k(0i%EDLNXF!63=UqHFF^V$ zd-;s#D>RVV$a*SDo478=$Io+eJ8?LRtl_X1$^)&SStDTFW-e|!4lP+?+kQI;mpv2Z zCI(zqd1n3g2a7~@vKOD0N7Ya4fJ{ zLj4%lh%1c&gaP81ne9Eerd|KXfy?3JJ=)1#|hR~V2lOc=f+sO<~D6^UH zhGxc#$*WQaHIX5crujm+lZVnunIYb#y50asyIMF^p-dNOq=F(AKnaVDd{8_{yvt=T zFp<|AgAvQn&bomM3k7!`S`Uh}yxOE!*}+M4@b9mlzJB@F10VSZGAullx;WtICI^$~ zpjzU;_)_Em&#>#G@j)P*!~vS4#Ee*ueAHLDxfU?Ld~TLyw~JkQFsXaLXhB2h zfGQ3RhgQ?#ZAXqoXBzyJIAbHul`iWzLh6JUovA6hiEWUnP`g@BWhwh~q7Pj$z{7nQ40Nb3`mlyS*z`5D69a<$cDJSi z6x~B-9E|OYImMy_j^O)YoJ>g`nNB|)NPD1WvqtlKvMB4={u6_P&o)=Fb8RD{23+{N zYbYZzm(n=q1;7aGm8a3#^85)Ne9Y#n;rqQH!_10$N^s3 zauV7Gi*S;AMUBKvW+F|%Dcc(u`T+8Q=y-cwHLbjy51q=?HNGD-E|6$@|JW3kb%3NF zzb%xt_YPxWz4<>Tj)jY%kvT;eVDFYgHghNd_vL(h1@A}WF^8iFbw~S_1-&wYwx@5( zNHi<*H{fK{8Cj!7wamm_Z^}qJ*EW3ZLrnxgsuN&>X@EE{S%s-3kdWR!J^vfqGgfns z?@(bLq^#bOCl=dD`u3m}ttXo`2KM9qMW-beWm1|$D zqc*`ioa6w+rs9+!h&A#nm~E|)ADC7WPjVT#x1;@SDXfqvPgDRR#R9s?ifRq}qKPM8 zHq}It04a`I-=d(?fZKBJ?8Ft5=rCld*i%9C=~vIEug<=C_w?OAr-(_hqo#P0ZEz`4 zEL4FQIF=VV(^{pTGSc^Rf&9DbjD~+`FXFKz;2c0n-^bhCm~%G#Z$d zV@(1b>~cY|%=Ng=UemBh#*o8*Y6TQG55DJ|a}9TtALmBS*hVH(%{Y(>rfJ0t+A@3x z8*-%NP^^QR2$xxo%}{SJI>)(T@?U}kl+)pz&*B`(9-h{i4M=uYK_icaMz=ZPy9?mK zHC6+f&q_Z02ZyFI9i3y~5L%8PEN?jsEX7!lR(WGB7$Hv^DaeEdf0j1+Tpgj9RNVxE zRtP&uFz)$~!MmvgIe{`@W{c~A!Ap}_FY`+$6p`x6s2#4Pb_{(ovy&Hgk1PmAZAbTk zVvRujn4(7q12L0sAddw$eu zW+IIewi%*uQW*&cm8@Y>p@UAtHkL|{L}E%yI};wRl5V_kCJ7;l80Zk1KlRiazC8a} zd6<;=!61c=H`yDDOlzPC*XiEiY%gHyk)ksYKm zTP&UrE!t-GB_(>_O3FkAmkr|Sf*6;Vnn4!P+XV`c8aKQ{;7=8*lz1Hmk+Cn&tm8*% zA3eO&iUb(UBA{Y%%IRt@Ov~raFpcOM>W+_&89VmE&)o)tJK&Bw;%s|jtK;AE)1H9G z{1Lo|^6s#{e@6vod5sla*1Fg>hKpK>=5pw!$kaeEUdod@!VPbL@6m5eZF-9ePmn^% zA-6L%=^eY7g4MUHaRNM*BRafgVHSZ?trB|i5tcR}MyQ5tW`UvY&2X=rPQ)$Za#44O z>k-Ae`L`+vP%K0X$~D){FgpwADQ8fL9;z-T(4~?Rx(>WajV??oyi{A~Y5euuS5MDg zzkd4Ni?5T#K>ut(pr_)m7XnlvA%XeA)K4;cG_M$6+%>X%_AhinGR6&AFE!I0UGL&W z74%p3CB!89N^M92s>g}D2}IF`RzS-f%DOZA1x6w`=KPtd?|1?fLi1+2!APCE*koW| z=+(Vjcn`*uW#%m(-lRn!*NzN`CSb?ZRXhCxRRm!jxJ|GbYu_--FPGs8cE5}o`lh%E z*OX+T%AtvX!IC<%VVsrBnxYIf$0x9BLW}uA#@nNil(+{U(C80NqC0s1DR%CTZH(J5p-#sB~URs#Sc0001OWpi(CVs&Y3WG`Z2V{2t@WG`-MZfap-VPk7$Ze%WS zdCa}*dfV8MDEi+|fk^#rkZp=?JIPFXMa6-QB3nyogbXc@>NsJ zi@Z8ap02a$Retbf{LO>?Rg*8^VYM!+tSQRXg9r1fTqcvreA8^Id@@OjB|K}A zY_%#IYEr8<({i!Mr}T?%G|T7NX3@-wX){V*SF^mzXV2gdeqok1*>sWBb#7nKKXHqE zwOQ&%&*A$h*{q5mH#t9@Z&uT$EEneOMYWksaaH*FI%}>L#f5(Q2FCveei|kCUvARm zpPTC{TiXHS3P12ilh^vEqRA`jQ~&UHSYZ9VG{3Hk>BmK`|EkQldULU^%4uGkmd(w& zSY0MroxD@)IM#!?$fh6j)l99$*!;dMXPZSH{)~GK^Ywqf*c6Ld(0EZ^`oB%f<+@np zRR9?$JjhkCIbFyD2}^@9Vj10D}h!eDJMN@}jtSce92sF99`*RepR` z!f_h$+4$q;^7-d!zDBfFKZ(B;%AV$czHjnM0T>bRH&uCA!AAZugCYL7DVux(tD-S< zg3I$@*fze)yqV|)n#{{;QkR#D@NxzMfMOl8l$YBh#5n=ZP zn^$FN_N81q=kGQB`LDTNXN^&2OBQ*ydROHZnFv|*m}Ox+x!j?GjD*lS7p6s68#KF z+sGWr$Rj>q!2{kYcyF1l5EF0<>*NU00KWY>s|Km}FdZf7t77%{%>E8P)lC)tp8Oa7 zo({b}7(QOuT8c% z`Yv14`H;^KeyVznSbvyYlnUR~uX7mxgb7aF++YibKt6cz;Jepv-@SeMYI5@G_{IBY z&nIu5zJnoGd;jsF{(Ar0Vl`cCW;uL6OF!(L4?bK!{4j?94C&83^Y+Wv&)&aqU;c2x zch8555A{QQaR@&SzCHZK!|~U{ufMYIp1%JhyzadVm!Ez8>kk72aQH1jAvpNWf_Q)G z_Pwd|(YM^)s$5Nq6=Ho|Oo!^n!$bbYP#ys5kxZ8}UZ?>afJ5BbVRG<${Fj$`KdEzA zzN@BL*N2ZD&B|##UKUfp_;TLh;e3>@4mS0pYaqDgb^U0T*B_g5{U}=(kLs&@vA{2@ z`GNj^FlF?F+bnNN==0O{ew8n?>SO&tb%;l^26VVe@_L%BbKs+HlKeBGAdtLSxq&U` z+oV=+I1S9}aOl=mKFw#uJn$O{Tonq$j9!Kj;PT~TSouMf4nBPNdbkJ3xyY6mvn*L2 zCh3O{Y4UY4Smdk0a(r2poAu!Fa0ov=NggJThwz^i-vCHMF&@DEWwFW@&UhAZWnUL{ zo~-hG#^carDK?O=`VM61bw6{KNx+|{d$;iIZZGB4u5w^mSNQM&aJ)a1*U1WxAgpFS z-M|7TYP%BLuR56(^Euuf7kP7?=d0xXs`yOZ{e`z5UaW#dtg%l8YYx&(d5telud*teHpq#B z90g1OP@P$x)bPxl)iGln-c)e&=j%l_%?F6nBgAa@cj%y`UFAVNMC8l>7eTg4*WgcA#V-3DfKRc-(XQY=`k&5A}j8a1 z`E3PT2D}J}mKESggT$c#+SM#>l8YMyYLJgF$242n7f57u;ThNnDkJ81l`V7LQl8Xx zu~@+1vl#%x&4=x3remNUbqQiyvP6CiWL7xe2@&i>&F?{#T~81g?%r+s?D?DTPT<~q z|LWh~zkc`p8E)aT=cmW`?;5UR{1+~QX;lz&jej=8U%^P}+06>S-932l>g3g*o=ynL zOF@-(=W3CIpYVWbgX{1piSGi;-C3suNnjG$0<%(yS*QdCo_^*0IdFmD zt&tv@7Lu!z1i)N~Uwydzmv4R#*h8%yQh~n!OZNw>8DVacdHQGh8W{b2HN82VW~&u4 z?DO)F0qpJV;a}B~v@~#JYPcly0t1TH{6>;plp7?AjKzwQ9fI7MErG`!Mwdt8Clj>=qq(sJJ{1s@I=@S0Ll@h(c z!tx?Hs{_Z1?*MnQ@5adFpV7%Q(u5hH5;H)0+N>8jw=$x|bWH?B37_ft`8gtj(tt4* z{2O0x15}PFEW0S-NF62L&>wJOPl?tYC6DzFe4vk#C+de4o|0cacp#({D`qiFq&5>^ zxr=5p0Ul^Ehq)+fqpsIOJrsbYzKwAtL~&k0ANB~~`(wH(-JgCm0Mxe!qHpL+b?wqH zaZ(sAzx0QiltjzCxhiJ{3?jo|aVUj5n1C5NK~~SaWjGId^IHrG05zmjP!NZ59yzq; z9UzemajO0x^f3dyt_tLhXrbY7;^DwizXMuu@;b7l*y{p&r8Q76$Mt$qGy^2@f&k%1 zfgE!;>3*U_?)dXX*F%E-pzGNKPx@`eC_H@%sT99+D3t&t1=Q|O&cG6eYi?!*%aNn? zMK~e?h-=C!GB|FeBsijljlx~)U5LH`+%X)|9RO|@ zT?W8xMAj2H;g?JaafMUN&FQa8`{U@~Vx9TY?=cD(3uK03zSNK=%Hod(rzY{3;LU zXyxFrf6*!77s@x~yi=e;k)v&SLO~=MWHkyZaDa&enHlLQ9BAK3@?}4Ck6hb}q8Z&I zBw!s8U3MQB^4xM+q7EP+Iea%x5qZ@6q`Oes$eO4-tX=_3xXy$1=REU;qcqD|M=?sYqVH+LyI3u`k=0l z={FG4^p`F!Abn&@njSl@o4m|1{56yA5SRe|V zDJEF)DpoHQa@jEK&z46&awzFxjJ|vxTLwR0w76^KCTSH)_g6hmw@&|5VmLIZ!-jPAuMWsM7J?ujZ_q@k5PoQrMuujS~V z9nG@K0Fy4B4!9EXPEf|aV3GSgU~L_u7cvAlMZTCNLLuet&(=35ura9}Aua&Crd4Tb zpj|TCJjI5|t+}V~ZUMlZBaEp*Az)l{>N6ZsCtB5U42rWzp73X^D@V!gU2<^1t_cU& z`3O%hY?=3oqg*Z+aG?W;rN~EYxXD+9rk{0qE)Ln<{l#*MGlD= zy>rt1(Z?6EYZ1kOf{QXhNy8d#>tmiKii5f}??jPYUX=ZSs-3u%Z-$^FZ%V#TziobN zADj}?x9_%Q;U2ewVpT%`$yEYTqjnWgTlW!s44W1$U;koI0?-UWtAaz-(21~hrQ z^76bPMMuf=Rlu`ZFlaGe*C~ro`o)k9(MPB+L~QH=#b3@N^EgX^JD`?o^W%*G7r2#1 z3c+IUqmBqklapC=c^gA{s%%1a9U5V zHmi@Qyou{uPyvK3&K{rB-F+LmxHSlF_~Z~}3}--58t%UJ`0zYBEW6HVM;_46qvQ&$wm~Rqz>h4bzejRxeFJbe*W1 zO%V`04WwWvX7!JFs-E*tbofzg>c}``;}qg@RG=UwtB~jjPqhizUdU3eD1h`-n_O53 z)X2u-?ejpDeqW&S^>eYt(pD0XTrrXk;wnF~o1U-e5i*%deoqPk>!#q?3*>ewl{Q-m zj5R^ph#YGIh*A?6-Fnl|MR1yHdygFQq2v6=CJ20>E$OS)mc^2F(AbA-N01kcQvZv) zmlj^WEH~K!irS$whwci6yR+`(K@(9Nc+k%roahj)VlT@-O=GkuqffjYmtH^~lcx<* z&y-3LzqZUy;$hq5+%tD357D-%xvz@9LfxH29TMc5fj2AS1^PAZ%avHqD)XwqW_T+8 z@&-dcPRO@{s77kfM>d;HV4UiBz&97ABU%i1Q)cLpjuzC|Zr}opH|rTv5%@mbwguE4 zM#PPsG3o{Vc?N2>YnRd5m*PVPc6OJ^g~8WoJXd#pFAbkG&DSY!b3)$ z4&NUiAFybiDJOjFko+0gl|lWbtq0njjUi4q6~+>nIFB*(QJ3g?0k>{a-_#9xYmq*l zJgjP5{@DhJ5-b(_Q?)EQP1c>3xBS!s)zwyCH_hUjsU$FXxdnHbw_U z;J^k#K5gld`uP>3UEgXb#Y$s8l|4VmcmyJ<{*@;$9@vvk5pJ>uV)rWd`#-4i!OZmF{Qa zL2iS);|_YA=YV(UeY07i{|(@#&|c|L-|e-qDcE4#ws!U)lE#glZf%ctfdh)+F4-?y zmP|K{Y+lG*+u=&H$V2UWV-YyUnTwa5H}9>4)a&WPJuXp;1x!bB zg2WVd1g)ZY-7K?@IcvVI%jzSvLk?5`Sue5%mA|#(9uQMh(1a1>$rs?oup6;yBjpx=3a%U@b~AQzb(Dd5GOeeUq4H51|M9~C z9;FT{bp(ajj!uo!bwfKq%o)ZEn@|S05@0kH#oxe}8(JdiaxXNG3{!KEHTz}o6dqw9 zuHG|rjL28&9l=S zb(UWR1Dd$H;r!Zg@h4bP`?wSKW!(6ybU>u?7&6? z4c@;xj$1bNlh^Y(5C|YcH_4+%^Rj$&kyTpvovjjhlib*rWsO7xqb1PQ4ZCasSQsi7 zZGe17a6sadq~@r|XgzMRRV0>QZY(Xl_Jf20{(x~8%}sIz|I&Y=o$H;{Vv@sh?PQ|u zg7o7Yfek|>dngEy2GG(>-h23oQHl67&r&tL%BLT-{-sU8Ks;VWIoE*X*k)2^8J7=y z1-cszE(=m&P9qsXbQ1 zHivf7(jFS^Y<)=DmouZI1>Q?KzN3CiWY!qZ(I;Y{QiV+B8HJM^Ehyyxs&rLkw1JOC zu0t|)vF^0JB4nyFIe4Len%_lszAw3k_Ijy)_`wD`U4S8O-FzbeRkf6JbD zY8qNJ7s7c=bwf=AQ$_;nbylq~mZ?RvqzvM1swH$o#3*@e&uHL|1Jz6IlXp%^pLKW9 zn7eW>hGCy0Gxi?=PiL zN?(j3_z+pkaajKf2wFe(Oz{SN z5p)(@^_auvS-w_gSVQl0VhfP0vxSM>BnKHMj{9$v0|&DS{f*wh_)&lWBC6fRrkr3p zEz48M;7Jas33!sWzLB`gDHj#7ydy(7K~K#}r|tjAB=re0hOffW=fL)t93FKHVc70X2K#Zg7kz0D_ryL4 zY)J4=EPnok6oseFj3egph0A<3;Zqa2^-T-js0m*&`dr}y6|n9d6)7iiTCRak&ox#* z=xEJ|7mMOS1a31Cr;(jYzmm-wea6S;eZ^Ur=bGH1E{3lF@0p4Uw-fg{7#7I&WsPv; zh2pkVG3ygeRO~sD-ziNO9jQC_^%m!yxLXA_UQe(5ZuCneq}e=_u&_-800(z&s(dyP z!TGHn?;(J}W*IGrT1~<~N?>Mj`X8 zO%PY5giGHV>n$R_j>qG3%x&iV)YBJBd|GnEMYl&~UzjWwpO%xNnkc5xW=I^UAZsCD zo0l;!-h?hJ4>QbF8|%_og-H8nysm6|x6o$>C60Q+=~!wV8SOQ>)EN#ZS9u24FQ*Un zQOTr@)4BHiRoPEgpQh7fdR2bR(Hj`=Jq$90#rP!ox_$Wn>J#bM=did7_!9%#(bj@w zuUu1lAwYZPD^j3w^RgUg<8|5%115tB`&i?TiMdEaB;L6qK>)Z|@FTn?3LUNh9GPJwWX1TnX7!jn7cop(nt-gge_TKe=)WepEaQ>o_ zt#Qf{*sFp<=P4BnYxX!n(vmX$s?bif8=15%29(LYf}B@Yf%pOql^T)q@F1KEnD@sz z`5Rvr2Td}?1fqYlb}!8mQa?D16y%e<`jpR7GopGky}~i1nDqzs)FfRk7O5p#=>?$c z6(-J0$?J$&M+%Hapl>&;cS><-UfBV;9{H8PeT`FWg@|Rk&RmC5P`=T(Aqaf&9=?a+! z{w3?-?MSN}M!H}B#x$xD>>~8r>C>ZvY>!9eWC#hL^AArx)yPw*TyyHoHTiO_U$Htk zE&V$u=g+6CD!@Sd8buzJJ&9t%lSMhr7PfKHMs zkdIzJdu>uzRI`I=Im?r)Qse{>mRG9_$XB0=s$7wi^t1$y1gN~o6pH}HBsE41#XqPN z88bUjF-e||pMT>7)7d7El4Vve@$OPKH9W>>-K4yZ$cZ+_tVM}99@STvACIH=eU*u^ zh@0F>XgW2ii~zx~w=`lR#VEI8wFz@{Xozr@NU3>Qq-=8QXAayq398ncYF*a35l?lF z!BO(&`x7(A43;lnm7B{eT7c00EzlkB?SaHXm|upJYXw^l?7~qB=jz}$VK|qW6iVva zAU~!@!NpVg$S@Vx zV7Q(8)nm1zClXAP78AaXO&IU4xx3nPr9R{I@dF?OA3_9FXJyZ<&6B;L5fF7I#lLeN z0l?ufSQwy`)1PB;s7E`>v}=)Y3_C0oCZUr7KbTUEZv?s;7cNP*$?MP!P3H3Ex5FOrQ8bn<<#f^5LrWBy4rxNeKxoP0W!5agfLJuGU5DPTViUo**W8|+Un8am* z4n&+?l-5M%`Pf%}5Xr?&lVfgvPX9*;p%M|*AbZMIf^FHC!$D1fO7}_UB1O+6#n4mC zJ?F?gI7#M<>{6vTopLz-BLQf-Uh|K1tFfOoVa*VIsfd%CF2?z1&iH{Vh#CFZ|C{9? z8Ky`{Pp`GsQ{|LS1dchmIoXxCN&dh8>;GoIlg}B5Zn<>E5?)2fTdrm(bSS4nUY>Xc^g>c=*ICJ_3o!oS_367&c!AkN zu*O4Y3ptG6(1CDBIV>&Znj0n$x16@Kl}V88TL$rb?ZGRPv+?C=Az zmLHhSRXG+v2zoRECh8hf1PNfA^%s$m2|~?Giv4+s9()bolOTM8eueQRo1n_vyAx< z;!f~I;}%Jtk^%ypsB2&)6LwgFCRaH!HQCBZYYd0{3e%A1lnnBBNiojlFibZ9{E?84 z^jbeMYmG{*vHkOjGa1U*29!06O(vl%(L`2ZIho_o{Yv73Vyj3Z&U_|r5#}T%TME!4 zN{wz-9vwpEqN4`D&I!|XqSU7L8SVmiMMwT>Y{53ul9H-XMp>DX_hRoq2IH?k)WgA9 zcJQB15B~T0;n{}|kIw(=!}weJG8}(xfqf-o9E4P(#2J|LAzu(J5)6LktXRxd<|6in ztS-_I)wdszvj!j$_LoMF0mnQGl_JjddeR^CvBSr4KP}4x_}t+IU-=g-z_mN8sRoK? zx*!&pG{rWBIC70N1`{6*KU{<<1@z}9G2&WiH7XuPjMEZMIj|%u-?_ehPk-DLaK?_y zX*txqi^sLdvsNl<=p0riltOAI4&lf{2W~aQLl`_j`YQDn3?k~C!H#DeQY{05e^MhpyKNY_qWd^#|+ff-z5`orWir{YB@pyvdaq zoe>G7+}D(w66LZPI;4~Qu6*w|R$Lu<5Nq^oNBsgND461zA;xMoQ|Y$xue!txVH6p- zV$hiP+`*NWFt3zGOlYCUbfxn*DmxaA@*+Nk4z%(bjz$2xu zpHhBOeKospF_XTyFD`?fX8Zi%$@A8*x*J-L2l$3N)0>DxV&Ye=~l9*E_ndSE-bwtifH4Y$T2F>Rb+Wsuou} z&u!2_)fzv|CaG8H-UCD(giwKJ`u)-4@#AmC--M~Xdn@F`F&)MH2P|aCueq5fF!b&J zSdDrs=h>=#(snp!kNlioZV(*1Dw6qiPLzZEl9Ih!>tILoQx68iyS;6RuJ#p{W}Buo z24@wXZz^4ogCwb7#@pdNQ#bOgXb=wGDDc*{_jm(zUTnSHm}(61P`&Q}0D*Y2rV5)~ z>XjC9pDO+U?xN3F7UN^>4KGX|V){dW`6f`8+u;Pkma&aZsa^|tVbE(2$jObWBitHL zcMl$*cJ+aaWy+~3VsetG*N$WKzTRu{kElJO=e8irSFAFh;KvFtDi8%qxp2VqY>}Uxu zGUHS}4Nd&ooHWG}(-bb(gFR)4Hv-;l?;cqE4(tCA5~stiV}c$7{y~Zk3fuj(A)T;O zLZ4A7|Aijo(m*>$X5h|9g}fE$GxRoYgZMGO0VEWh_XW%sC_hNCp*a#8cZyJ&(&KRs zEnH>>yWJ9EKp|RQ!ov#n=_B>kFewZ8lONDG^E<2rrb<+g&_hi%bY&ilZ8tqnsHAhq z$mqd`0+^Fh;XTh;D!KeRmqGu4 zo1~N<=^A)s>$|{(Wly?qTVSI763d1rf5+-_b#|lb<7rWl){%sYNJum(Mv&k?x|TO();+_R6P3dEWC;0`ugHI=@=uQ723uD&(;Vl_q~N@VjU zXYUgb^%C^VxI#^No=ZD2iu^YkRV;FpwxXmf_XtVJj)qnPQw&UtiQ>g%w!ooQi5#CK zM7}O{jaDAR+kB3R7+FFE4v+j{>v7DzPUNZO@9g&Z_@93BYXR_>m0laxZ(@S;6=Jpu z!DspOupfBszW3W-TdNF)7Yue4Zb9IA`~;<2X};5+ouO$_nW(VIq~&-YN2n+^)>1UZ zxz&Siq1p)4(5V^)Y&9HJKh2#r&rXpm2~`OoR-dH@7 z*=(0y0%X_R_q`#+3BGoafuMEspE-M8 z0q>1!Lwca#jZN^_7^?{iY!<25te#$EVU|v&M z_+Hmg>EYjaMq^9QJ+!;2F;ogus7PlE^|N;);e5uk4O|Anlx`!Fgk4l{Fi48O#r-67 zJCxa=t-I*zMe^K#gmb`up&@;L1b&7R}w`2;mK99pa?v8a9Y499oPwy74?3o=1_& zP5^7wBCHh2GG&Q74_~?GaMshSpKVC{$Vy&nZB8y4ESVCPJ z{i;gFWOr|#Eel|?;6((*FDXVz;}j+JKwOWBU=Hm;Ef2ZrHd*}<@Gau0s~6Skf|`k0 zIlE*U!URUApQpkPP1*xn`)EmO(b{aD@)py|+xC1^CQ46-$2L9qoSr+$mwGctztxGo z&ip+7+>KtBR{nunrRU0zSd8l-PsTtMcCl@@pEW-CMnj~zJSf5Bh{O&9Id*1fbieL` zx7^FRv6@+-lD}ZOHGK!eje8NjVDY0}jd`3R{~{arjMd3~(-kVMJ&J`5$8k&J{usRw zF~H2pTC<;gpOctm#VYMG%AU8fo->CXafA|vn<>AoeU!d++!Cg30kY2-1B9aqJKU*? z%Y4%`LAYM6BS4hTMySjcC4HR$KD#^(1{5To=2miK*g%u~wt&G1gVy=N8347%K|OII z@SV~6H|Jmf`q#tcFIP9pnFN*-myt|;ux`ghDR}|(=Fm<0t#kXq$motmnrCNk$E)IC z{X5H5qbnezgb{lz`_{^J`P&I>?`L0R!A~^q1BVIR6An>zL}N55R-(QjmX$1Owdex? z(xMa-Yp0D5v0eQG8q^N$>Y z?RYqfz;`K6WJ=MtDD6)-Rn7WCO7w{_6w9H_qQ}SPVY)_5|}lWu0rIiFz{V5=F(wTb8iO9zsgdQL95;Gg#%Au^_)A z10!W(5DT`(DAZM>8`bDr;QH3cIlzG?I^1_9T>n2sS{2#ag|ZU0Q?w3V-$v?SjZg-a zeC0NR1PsG?1OT)2q8CPX6Y%t*)3lW?yIP=Pj?#owngYWI0-^ZSlrpngQdaY_#PElFl~jE4Q=JVMWP+!_-RU`-aG_kAZor>CaQv;9b_`7I48^bjz|WSb2Ztlisoh; zwvE*E4im{`#q;UOrKFdl>#272NPQX4{pvOjQ=;-_D1hYP?O0Qt#^@!pyja}m)*X5L z%xK7pBiytoNC2)U4dd+bIoAFfFUsq@@{R~*!P!Dy5v`q|fqPSKR)zoc*ZP9RO*8pKZovD4eZ?Q~B#bO0@kg_dP7NezW0 z3nzY8M<|2mpU!5rB$u3KO>U7B9?EsyO{_UqW=y1BA3rBLHHr%c^@aEj1M~8phG}_% z{9oz6DEfHv?Dy$mM7R7eAU;xdp5Mow5(qu;L;jk^7hLNLD(L2n{y_Ce{u$LoDXw$c zf-k8>HhL9K@xQ%)j&GR)P-087slh7v=lm)MP=w42;q4LF84VC&#ix`+nyUP8{R z;(`GVU*Ux#4&ui}X3mk*##S@SLAG57&zSR7>;`FoyKqfMG%5~m1=kY+35~qMF@T(7 zVzlMV?BKapQ#i}!l&0a9!x)rsqqoBg-f_r`%P+)_)R~7j+xv9d58GHj&d;|4>&GwH zntq1$OSG~IpP!Ff*}Dka(|y71aCIre_>=xd$@sl2`D=T`(ofkp#N8S36RPo^I~lQ#Hdcn zj~+Xut>7}5X8XkK;ZF zb462Ri{d}oMHh>X`*I!M>xlT-UWdUQ zE{@e#nA2D#aWojyVG456sYJ%Bf12Ue*DbxOFTif%S}7OqV5G3*+OUPboV66UIiV2y zd6H`e+8~t@zLtr9$VuAxEOS0Kc+~*npmoHFwlcHsG5S*$Ee&m^_V=vXjYy=^I^C|t z@`zErV;}W(F#u0Mu)lA1?uOp#(UVwi$8M!bLQPnV*$<6c8&(~&-f@5`#zW5w)RW*= z%0wwDN^<0+b%NGww674-m!bk*Q5#oFg*HchEo^cQZ>rqL;tJ03qvMAU&FDG#0GTO% zM>Z$a{+QrtH{X}my$j=#)uS7eQdt$3v;e}EWas7DH99R3vC~OMk-BoM^qsTO;ot(C zNp;g}MwfmB*dvE5MpS?OjbCz-FI3$FGaEeJl;r|F-4jgxShI5$WiHE-XQwZc0i{>` zbGFEaNWz`eJ|2xJYLz?z-2g!#9+_OS$EczKw6NP``ifqGPJViI49Q^|4ds#gi)upC za#e4Z>|BN|r(_f20^0CvxoO6zBB)g@O;mMf#mWQb;P1%LNnr_sYe0oeZdM;x<+YK? z;D}zkQ6nxoj>5ehC1IdexHv(eA~_7`EJr@M0k#!sC1ua+LCAitG4cQ=re$QDJ}>SL zC1N_&2EiDG4aVJRdhLP~ot&S)yheMLPNc7@4rp_5~R;)yzfU&)z z8G{#(iMmbF?h1_AN+m$loPltsN0$pf4+p(~=2F}%`eRJlq-&)DX*K-?5u+ATMCPSu z8Kq3qLqt0RtFG*u!|`UlcFd3=1gi50Us7FtS-hV7K6%U@rkKV)4WKOROG=x4Yrx%c zlm(XtWv}1&cA0BT&O}v*`Hee(8xM9(0ID>U!zsH}8-OPS?d6JRF>!#}I~nzP7>gen zuFg$DU*K99q!hV`j-fCW@60!NNvdl%>XpfOulrXyRIaa(wbA5302HJkwH)H$GB{0m zIjse6rd4eL!)S^h;ZQ}&?IN%Bm~q|wKF7(^#&ho{uN{o>8*wK1Sq?lJIZcGaqe*KQ zQ?c%Lcs`*fy5_oHdPIgC;FZr!Os(l>g1J`SIsKh!@ZhWl3EEU?0-e|%( zLbKlOAn{{Ks_`l38O7bo4Uyj3x;;cUz9pRii4$?wi`l7;_0rdrfg9U}5Uz^dG0l3a_ofnwARQYfugE3`~9?!3ad}66`d2>ZoP$(iJ0-gyi z){Yl!Oc+WXae_5#B!~)z!ag%%2InYc6hi9c6BcbIs>tD5T*Ld635I;wiSs8;K_>{K z%IxaZWp<-zAeV{Bm)4dg{q_tONV_Ok*-f0-Q>tra;zr=Q z?uQ9r)-?r{)}~;GasfDs5)L1yc*$!OYnZN>ph`1=r_1Gb{EreE4YM*LS zIDUOqcAbg#3GM@sWpI<#Dm`sFPKRd)kI!`+UJ)q3nDF z3TqcEZsOznp5vcg;mu_?R$7|V8Vjk=bz~&DbfgCwsjHW3zHzwSw5$+cJz30;rViu# z+Lfz(C5PgY4uS}kvAfDj!L2D(JQ_(T@hbcNyBrwqG^u__UiILpHMQQRwa^Gr+#iAADZ1Eu*}+-)jj>b_)>TQ(Dh(0VVxuA+fE?)hRbz5m zEMubWH6!Mh(Xi%$St3tGqo?j@S#*FPBP1Z`r7K=VEMcE=O=E3-P2L9J#upY6FcQ@R2P-m zPfjs?I+cq-|C72=5gklcNwt+NIAE=u`(zeBzVk#FN9Q^$II+pTh=a}p6JZV0Uxh4a zg(bKUaLijkc!DRtW5%Q#c1fGaTGx@-zwI&woZt5 zr%%ik@ZK@`UJ9a^FUh|tBl*L;=_&i?$XmDgw7M`!JDJwajJ&%D3HXLJMHZVl%|%FOIzh7OLt0R+0T>A#W~9W$P_Xv z8l%?g2`9f325J_cBOIA(3=iR?=o%g2U9m%{Ug1Hkz;Q%~+I!3&1KToraq{Y4pTAAv zK2Zmztjpnepv`XMViiIh)R*T|8L;e`VNR~9S^BBDei(o1wCS0w^8QyOx|ta zjPhi=CyNXxl@O-ub=ofiHaZ9r5UR^)>?^@$wHKCkR3;~;4P;B8q1*SDx#hKB5 za=fXqObKQ+MQer%favTh#5LgKnb=D;kbmL-@gF)cQrd@NIPQ z;6+=>z~|aIRg^&ykp~+W6KGybtY!a?q5wxBQTVBlIRONHKjs zmsg)Yd}#mpWdG`UJP@K$(M}mLuqJ}qY;F1su(1tZYX&jI0Gm4OUmpK<{Ny+AzkhoC z&%K5k4fWo}&1_2)(0dCz9q#*s+2V}fACR&Z1SzFjVloU#?xy)oJ6&~o1}FP1h6qXe zf~m_U97BhX$<3{B6XapR4fBBV#QVI^x|n{1N2=ho@VddGG2>a*q<&M?M;JiYrl!tR zjD_fGXi_n!A3jjA)-)JWc2V!pjT_F7?z$)h2|Yo4+Py;NLdku5G9gr)*iXV$d+GwaEW{Kx7>d8{u5IMSc5 zXPo86rv9sP1fgv$H{=^Z-s01%Y<0;=1GSHhfJSY}uL8pm@DB3a21s)w(8*m{T(dDN zt!J}AefG_{D_{{ntkvbbkXF{CBg5swrSz>y?Jxog5Men;ak`#&I2{{FO5hf&c%s8vp_$Me^KeXP8wjgE*AOO zyYV|4g8TyW1!fE8`A1gVa_X^?U)Q?IrcE);i7a8&Z48t^S^PVGMH$Rcr+^hrm1C85 z-V(4Bsj8Vqp{-@0s&3pQf66{(NPVpPXo0s_GF<>xa}Pt8+N`v}e@RYH9<^aht#xc^ zv2n&zNjd4WVpW;Eyiz+R)57%1TeCpPIkZ7n;da;iFNCOz0lF=;Ol{2o)|Jm%di z=HD)8%EP!V9S8&8r_0&UWS#z$E!=^ba;QEe-y4gv(a_}aqH<$>M(wEP0el#$LAbqR zy6SkT*DzSO#DMFV@|~#?=S!Sm*)xwHmVN{2n;%}k`u8*j2Aa`jO~xCfGMCdpEwnMk zL(}8;r|(|BoE*P?`SR(jXOm~o-&lGh%`O4hn7XMQ?#t6ZAN#<4i%a|K@~z(7gv2)u zn(T&kgjYb;)ib#Oj-tyDPL9NiN7M zQlPJOj#D|=Et=M`L`B;^s<_LMT_R-a>}KnFmlPy~1Yq|r<{+~iQEo~HGXDHiz~-w@ zdST$}eP+HQzP+l~bhmFUy%aO36G;23&TT z{PPIGl3RZ1ZrF8T1Q3w`E;zGP3mR_FFfWX5aOBa-iL!tYM?7X0o6y8J-7O}wK3XD2 zkD)qsL4j;PS#v3Z`;}geys^3aV;>R)P zdK{R+4Y9U_LcX5H&yHLr0{4oU!?IfSG#REC;nEi z$X(hAz5Vw1$B>UAOtpUW<*$garmj9`Od+w0!;oN*=MS#N&UQyrtk=o^M{hgjdV|NU zM7eeda!YY?^A2*ax*rSxaBRAWoEdvexsgV;4st6qXx@ED^{Wo>m2_m^bl^F4qI-kH zw+|yQ4P=i9a`|yS~f>ktMlD2I;zpN7C55{fvNHPHjU6Ih2BrIthz3O zaIcnKcC2~wa{=`3<96<^gN@e51$ep^?8lueR5Ea;JF4RLdFw0caJ7Ss4#=i@?GS3W ziwC{HI3Mry3C@gwv_sh{p6v7@)?}vF+85TIsFnr;+%3Q!d)Cf%0H?txRb8%|jB;K( zCZb(5Jc1!!X4b(LG(i{0dbR?JI(D3^ZUv@bV z?8nVTPs+O6>RRA^m$?5jpK91W@ULUd9lA-JrK;23Y6RouR81)_u9Ao1@dwf6S8w-Q zb4RX9@wHZs-m~YY#|Ywt;o~~d*xKLNl~;s)6@x!GSd|A=POZ|If6z9`wc8WJ+H&(+ znPl6xrOqqPbFA8m6wg9C(>Xg*RnQEz6Rq2R{JJLCOAu!ND z=RaXT(CO84$4^Zwc&;#avpmpBmHOy$2Rd)vQ)dX~6Ul++t&@n!ee)4yx} zxsCiF400F6eMQAIX`YVNA24K{YLwkHjlb16^$VTapgr~)R-H%2+^av6p`oKE9WwgU zdO{z{EiuQ@q}07fr0*QLlM%gTSP=@lfw88#0W7jFH_TD3WSy@nj=HVM>lP@p_a&!6e1AhJH-zUnN z+?(I>wQ%d3w(4{811nGv&-@i2IW9ta3~wUc{0cRlWkSEpvoY;=W8t9OG_URu-I1kL zu1~f9XjdhkKD(;4Y_(a{oU_Hmvf^tx^CD+nsyQ7@a*YnD)NliwA|-v9IeC0k4_^Ib zQsWj}`IWr#mu20+rq2P|in6bAZ7g(}DS#tvFs+fDv7c@+9jE8pk>u_3@1MVV{`Tp+ z=aWCaKY8)22O3WMCALW4>Y>s7X|fj9J-Q$lrK8k%{Q#AMZT^jr)+#=Q_T zx0QQwm%zPE-yj5qFWTs$P9ff)`mbx)fe5uFneArB3Ifc~-0@?EdeJ&HnC|I1=q6emB%vo;o7cY?i3_ zgFL~pi{xr5Yimg21-%!$hoTmot+Jpn z2{ZO{fy%354!j$=O57{hHGTQ~-P41USKlGQ0!H)XyXU9x#xK7Uw99mY4s@*@b$U5E z;}kSW_%v#W@CQ+@px5^5XExL_cnKpeVP?SPl$iSdDBU#kgWqrnrfwW&RaohCUt^No zlpQ~UzZ%4t0ziQ=#yvVf&&R_gqP(51*{5YIGT^c;V8RQJnlm~qgppqk@OYYri21mt2)=jmUVyM92jVajMy=23Y9nLTwOU%l;r*FSMO-E*9`sP?ljybQ9 zTig(xZqS9fD80N3Zhc!Nbh`CzyRWNJVlO&*&qdAgwc*^L$NFQxKsp^8D*e=?)0znG z3thj$>OjQB2>RE8KE#92gH{KB8Adr0KGx)@UWHtZH}{+H!0Pd@VopR>y3h7R9cK6@>BnS&xz<&LkfV#oEdfwahOV~*qx;?BZ}BW?-Bnr zO7>g@+1|)o&v-vk#r#kiy)h6?crH0|7ewA#^MuJ4Lz+cH&pduFi+pBN_1vkbdGMTyRkPRJhXb8nUw!1 zP=IL+&LvJzI)vRV5xM_}Laz{Fb&>4jT9=!)u_-byRxRyGN^)}eLsJR9?hy?>6d5tJ z#L_`b1;^$JLvVVFsg{PStx}cXKAdpTj8l=-uHCoQ_4WDZe9C1geVeYw)1?OrRr1fS zz3jeuCR;mDDF~fTg1}EYLod;6`26!U=M-e-03GjZk6`qTm#1f7Zc{!b=1+1u5B4Oy zetp?r?t=r{MFIJgPG;YWC=2#Kbo ztKMTw?RRjQs+ZZ&t@E&Xh3IzMH8*$E~P<d0OJ4UJzb zcPwN$3TntToN%7G?N-cHF|gDpD|Tb%8lf|0R7Ko&Gcb1Ln~b3Gh{o;2b+u!*X6b%b zW#X5)jqZKh^e`hhHA%EpBFa_US*;gFd1#fg=gv#z#oybD+|WjcItMbD;IMa0-Q-`8 z;WY5GpS%IqEw4UNHAl3)796_372*+_KuW^8eOTY8Tx%T}Zw8iIC6eI4^cO-fXD|pB zJ$OE3_Td|AIEoRP;WohjPiKb*zanh}{gigTZkEP>5H2Ka-2F0t_JQnd5&Z4myg3zF z?!y>paJzB_L4O^5fPU(*0-LD%BrG5em%L;M*I!5z3M&WH2(} zCa&y+=A@Y*b=2EVx$A#uEhR5m3-%H&8aTI{R+Ay{%cl&U3@N?aMyD8mU-7N|fwvH; zQ(TccTJW7|7h%9C8Ke4(=m?W1PNAaf2pMocz%#U}UchswvKqb<6onsK*6A&68&)D| zaD|frcgr18XZ0}2v*{HQP^#F;bxURi3At$SMS|#1qmTIw793PX0}cIRBQ5qJ$XJYg z2|0x$FcJ!M5g=8y6C%8JcEm=nkqca@r6q%Mbp3IfhdXSerIMJ3UXV##sV@SE7rPA< z;oX*ZVI{uENcI^!V-@;bcAw-USK;rQs^z_=@tcZ>d!wgQmbR>LHZaCFSPvY{^X9YAAY&slm;e*1GQ7 z<`>l+*WkS1B3zg%$61IGb$(kzL(imy4tss99sXo$D=WeUN4FQcI7EWP^x-(0Q*v$3 zQ09V4i7>^)PW(L%AzJE>(ARIT(J&xi4idssT^Vv57q`Q@%Z$1FrqDyh>;HC&^duvV z6ey4i!rOA^#m5iBQ#q9Ig`idct%$&ja22wM?5bQHAcLC>jvpOAd^j}MM)H?(l@i)w z4Om>s2pqQ}FLBDI4k*bd7x|*R9!F*rLychchSWv6er75WYcm3pnpsh2Km^N89ow9_ zMFr|XU6|@m#K~VAsUPB_Th^B-EBSpVx8LA7r|^Z_$N2U33Hp8$cXDrNZ$% ztkDAPFo^LA^DO&WwF=%I6Sgq)P^F3M?N~89P8E0R*BQlRN-H3D*OIq5eH5BB2V06n3k&J~)f zpZdF{#g(jh$uq{X2a_UzNb5C9jZvi+T=`}WT9s*lxWon59N9|>kFl|5^0uphg2Re( zksP&Vdc4wf5RbPRRgkc5Et&hVx+Q+V8+ip&HAe$s<;1 z0kR;OWoKHvg@;NLR+C1{113jcb}$*p`tq7DRb#f?x(BKO`C?>bJjEU>=tb>w`NI+a z5~}Ic(q9TDd=-g)Z*%giu5X?bLXY@WQo$nenalUWG0%z9MO344%&>H9*4kd`zF0!exyXhtc7Sj zj3F`8AF|SR6SmF}+3nUO-HVyj?olW$RL*Sd1XB~OI2d8z&S>QJ-6DbLi;NfSD0u_4 z^Nj{(UmkNm)}+>yUUzZkGQR&j`KB5;z3b^tBNL7bA>>s6_l_b_Kk;r73u`7mhI3Oj zEXujPb+6|(gx6+DaTR*tyYRnVB>VE`joA;XQm%;-)%L}iM}_ic`7;0W>ieKklLPQo~>$PQgHvtn6}m?ecXH- zvoDSLbG|5j)zCh$fSZ+rJSw^wn2ux+=#+-zU@&P?O z$tPmW@_5j&rkrzgzEH2ngx>u`x$!1}o4XvWcHVXB4xD?CUek`^B3!5T~~e0RmSTe>>+aViD_{gnAKG{A2n#|5B6ZQDjFTj*g|9XlfM8x#uG?niEuEV zF0zWkjTD{23&G)@sNHzl0+Hn-D$`EZG{v)$9gT@JhrrzM7Ar(hEQ8_M z!@Y5_Y-9)^LoXUl!s2(IzbB*x9=Y>Q$Ky0I!Jc~-w;Ef0aq|7iTXstO5ikd~v1{`# zi!C(bl4G3jy>*u{4pA=9C6VYY>vQS&c6Kh08gL|(iGnID&uDqVLCVh}@lRguI0>Rp zutsdc3`9_T2dd}zDAhsF@&=8pmo~^0F&qm(!YY~{AIU$Ex6iabj+8KHIyNw|Avw2( zu7lQ$V|0lvx!L1_rYep*{{F2R%eywpPJ{ToT#O`Nc%HC`6G-l)6hfg zu_ZD|S!fMV_Q!~0io0ysr)?hgv7bC&QK}+R--(T@a^<~rkdiz##r z3$7?1Fkds&VjzQe_I>m5UA|mn8Q1e9pU=_r{ZkICoh<>Aj5kcvWBr)j`hl(yWhYhy z0(1T$n*-M?c?!VcBVR3*b}YWJ(~;5gyOzCBxbf7_5BoR^D~uSa&W0WmtBu0V6+TsF z|9n1AqtQI|4$;xAe&^2d&eKJx*aMU_BzGLOMP4ZRFd4 zmFV~N4|NFf4ut^Xj-)Jj^#fy7+z*5G+u6U=7HMO-OIKpVvM%i5GT8YNq`RnVB(~UD zP_E5)n)UFYzf0?`5i&IWk&1{lOYA01P^P-T;xrQSdn}L^WYKcPbr%2O&2@&QvdJd( zz>)AB(_Czxq+?W#9zT4DW;(m}oPtvnuio9AD2zb(5^hCmD`I7}nIG_4MvZU$C_Q%- zEgi-&O^5hJRS%vjv1V?V&@8{$T=vklP`gew3$=j7Da%Dj55+jB9S?)P3V$x>b6Nan zrW~=i4M{fd9GPs`q5=}*ldV;RtN3N>t172`kb6pPs;$M+2U6Y4c7|dVv+NnPC@-=F zL*Wi=LFFy4>Y_2d-fg+@6z0lM$dm^*9IzYP0I~Fbj?i%iWU858)4)v1^$z0!j z7V$}E=g4{eYo4!>xJ*Byra?btr+AE=LGPWC&$k0-T5jl?{>EW^v_~};nv||70t_(w0wZ?G+34ldzfRsfefI;h?SCm&v+_D*+ErAx5YUa1A1A=+ zA#geZTVPqZG?8f$1H0bDO)K606qsw|Rt@TId<$nPmRF>Uzl?5b$aJ3Zw-#^lu0SO^ zwk-_i8JQg}>T2vkrk-84FYxPi)LfzJAc9G-J*_@cvAR*PXMg(rFIJT>@&f8p7cO_hb zvSZ}Bz(ojzac$Vm9jSdeI{+$?c=%I@ZOwt9&bilJj_7IJskT3XzCX!q0hjGgHu2L% zI@vp^g=$KdMGxT5Ni^}ZGvTBy-H~{jckV$z@pFbN6d`$H0)5Gz9sYLGho8QbN`HEi zOZ@CcGCSR;T_VHF1zYL_9mqtsC}M6sggl+^%Eh9*Mt*ToUKVudU;O4bzdZQuH{U3~ zxa-`;dg4!91iBe0c+Ux3{N~ zNoFPk6=Dx&)s!B=>`aK3)kQgFv_iaHk@Cs$j`r*}Xpx9S3t@vwB|+H2^95-<0cDF14s7lCRmTEsqZ^6%m zeoEQK%oC-J>RIHRvgMNQ6|O>5CcR(5X0wh1y0U#b6*h%B za9&h36XKu_C+=V={I2z)cd5bfOmo&}W1bF|QO8HeU84~Mdp{~2WIT-89s~9_Yl)hp zffMsW9=eY>O!N_LF}cxwla24s(vSAJz*4YX{1+G+`N+(jsfF{ z%zmN$3ipTOb@3(3r#gt%L8AqDVD{s?bv(iN=EHarTVbY8QbJS2v2@A=V!zM003V)R3tG z{m5Exof+vVT28joqA6Eeyjg1?N}^Q<8xWOf^1z!32yW;es?8Ge2>Th;XXC4y&G3ki)Lcmu*^tzhuhdaNA5FwG~D0C z32`6jB(`tgpT3(se|GZjJDgv-KL8tlT~;6Kbp~wTGRGYHM~G+M ziF03yp1Y&yul7Yhm7!SPGjf}REulZT-5;23#{i*nCK>W@(S3SCC%2YZxlfxt#XJq@ z++8OJtU{TL6PkIACe2x?B6o5A8H$(rOqb_VFRG+G`08ebsy8fKNTU zNr(2AB=}{le(~=N3tiKYhfG7hIq$a{!YM&)Ni@NpZQ5oBcec(BZ33ju8fX1(ml>r8 z9oU~Z%9u#FlTp@#s4nJ1|Kw=E&dRe0lTAOu(>Mk2`X5Hl`+X)Ox{yog^`W+N`zYOk z#7mRbjh{8A(|aK*Xt?RJx+DhgtlDVCCi{rzb48x}IgwMA2Qm~VP!TD!Y@n+%21wQ_ zLNaSq?i%F*jmJCBXXy1?WJ$5aqTb?VMDv{82$4}+W|zg(`p)_+(G~&8I-y2HNB_ob zZ8^sA*2I#=80krgiCx{1X_fi*0vqiTPPZl(8Q|28)%vl9K2{r%wi5QFm4P&AAw|6{GhrL{6EGxSfIc9iOD&y&Xhqtw?GrS!*WL)uSW#>L~>7S+>g zZ9Q{17A*kH`;?}*2U<`c$}x9qw(7Yl-qWFZ2^Nj$=G?_P&&X!f#*szctg&S100-*6 zWK}|H{9%(&gW{r!>ts{(cCm?HbDZs9`*8`5N3jcK^r?Su$EBjz>Ax4LoqP7^UCJ-+ zqL7Sr6Oxk%%N#;RB#l<;t&sd$qbBWbR_AdSP?}?C z*b?h3BoK z++W3d<8;~pir2r`QcxitvkXj6!|pAyjW3FYpr+x2kh(~P!TjzHVf$(gTsSH=jIj?9 zRc8$EP@A#%<3x<>6YQ!k!s!X^1Xd~(8Bf`}pvA9fK_B)${41bzZl?Z*nir@Z((F^I9!6YV7&wf&+k;A4x$ zR$GOlHjyyKp-YoPDndej?IHsI3JVkK6tywuVuQJa`7Z$UU4|*4LK_S%OjTg*kqbqp z)i+mzj-&&vC)^3yPxvdFO%yqBHTC7Jm>0PpCyJl_gmn(A`#m_uSslRq@qA?!XD411 z%c5b$E@eM@p`USHeF+XYkI5*pGjKAQ=v8v53-*Y?e3T^{-w%?lq7cK2NL-3q-H@Hs zv$Unv_^mX6$fxuQA~WbFzD{k&gFNgzQ=aB6}ZdktwhATR7Z@iO5%rSSv4br z>Yi6K1i4GSX5n}lbAfQBR68;qsS45NmGIUvT%#TL(XAPlSBwuZbXDeEpO zH9Z)j_%Zq(8{bM5y`3ptq%cz?15#^xaF)g8RfEPbK~Rr4PcNlk%g}FK!Gfbrwz&4S zDJ_X?C!EnL1sx^sIx*OP(UpZ~CUXK2;`Yo5E>^0uGjowO=x8>Eb*q^<$u5c((VR8o z@p4jJ<|5X>DWlQm+kT>VhO9MD|9B#{dxYpiSdVy_V8sD)?I~6*d!#?nQX2S)L_5Yx zcSQ8!=Pf!ZHL$ds=mjMnp;( zfkz5ZU-1c5zPRxKmMh)2Ce=8ciU^@jAbhRz@^G>X-CLqJ!pq``*@1wBWXQYV_~vD~ zms}HO5&S*)VO+y+${Iy?1{r?WRX#61!}c^DL>SuM9#uEEChM9iny@aWAK3y$nOy96 zF?SSI0NI0|RChjw#*wI}*&ds>K4rxMSIwW$X`Y*$QL}PNsB?hPr+_e*k7z3%sXV|3 zmUdwv^DOr3JNJ1H2^#Uk!f^b)J%%x5@(t zd$DKqHcV_aqlt)tsDK$v@c1br1-&UXkm}XH$Syi`Qk1?yO?@y6We29}Smvjb+;?1P zUOph_RzqYN^eH3FRi=#eKdqVxFKD#1lG=l#9`|Z!n(-x1EpTd4^T^IV;&x^q#v~TA zY)XHlB^TN?qx0b=urY%(=syxEWC*j%ZD$}$kdvLhF-2$R-cVx_RXZOK!w3WdQjxZE#)zN~2?1#CI?)HFzP`0N=gmGW1*jeUJ-CyZ|!B zZgGiuCDHP@q44(@hlozfdf7+2s8hQ=(-4@>ihojR&T%^Gz#ACL%i2mQUN{`r2@e@dllY56H>bH{k4Z?q1XdAZMFb2b!5O zwKWL|NnW+0!8J<*KB&tKmy`?nC@`g?;xLRFBPGbVXbbS!eVwQut$d3`0=S0tPY7w3 zn?+L~({9v`D!*$`T|SxjTmC+{vodGz@4lgGa#7GC#?!cNR% zaZt`dN-4-n&4LUHVyK3gap`hV*B0b-IINez704{t`V;r&^ik7_O!a1sQg(=7u*M#v zvk&*oO5+vBa4U%w_Othq5*N?Y*^JSQ*mWbTkuCucNY)ypV?w$iy4A=e1BeG=WmU{( zC@0@IDp=<>+HL}$0Q!3h=?S4N{fk zag^L1Gj>NdL`m4Zt+TbrsYlGyxyz6lQQGx-a1BiN_LmhN4^#_6ra}%dHR43&d9=iD zN#XY3SjBuSW={gATtm0Bf!Djn*Ki63vdG02i_L0}SBPEwg7fnA198}`nyTy~;t9jr z?UvwFv2i`!Cp2@U+V|GAoR3FvcaBjZwbZQ z_O8KF>8YfVQd%S8iE}*Pq}`J2Iu1lmr@`Jx#I>PCo_@@x;W0@fOxt)4TGNgOXGuG` z)>5VqdlthBVCW1hW)yZzuv(baWf`g*FMBGkb(1PKl4^XB=^lIWHO+qd-1doe1 z@eBc2EM@PP&cv%o8XuxFcbm^SA#a3A&B`fofWC=RnF_r&V|CpLXMgvigzlhiB3;we zZ=Reit|_!muwR*SG#$dV5~k4Eex>-s9BJ@vB*k-WXT1^iGhbv)ldn{6APz8ycjCcN z*9pMctO^h-_*L`s!=&`t?60X<=XknqMwd+Xd70O*Rz z<(UF0t~Vg7eIAJ9tcS@<{&IX(7SlY8wN*U#o4isD2kGRy*Ka3(e1G!d8OjIg@Jztd zvBA>?oVmy_%vy__YKs~ZA*?PH)7?Hgs;$7hIlA#3zkd1Vj8*)Ka~t*?_9=k&G8SZh%v6Oi+Q87hmofrCnM|;WE>ml&T>E}AEbFMIYuKe(69WXoRF=Az^`HM zdX*=wXXvyMIOXi>&B7~|OFEVof1WV0uPQmkV^V_gB4P{)oof~`=Nc+*Rwc`ub?BtR zgLS;7T6TFM7$miU{IctH&2MXm!2hvjjLa+M8`EP_T$!4u6~D4JW$nu)LKB`ku4!lw~!j)S#*md90o$dh~2dZ_J6;1j96 zIK>}2)fYTnslT{TIc3EX0y`i4hJ{6S=3eK4%3{b#32M)oUO9NHagY&*r!!rzk*m(D za_xHo;v?w+CjCCvEbARh4>|fweBc9BRhu0vs;1#Y~lPf+Z*%1(i$?tX6 zS=B7GDp67;goCD8qV-k22BOXZEOu%F?AhVLbFe|dNNt2>ow z;P%&dSj!_(8x3dgq5wj5GfsNh^^zndU7_)W!d|s$hVmg4jhgzbKS)%#?=cekk=)gxXHf4S z9?ZlC2#={;0c0{fPDo33jsDdiq)kF)x`IjfP>WfGy$Y4ZAw)bPn96AqPmQ7@ux;?K zh|`smomM5@ZCQ$|pRg2HUuG$;S_)z-;x6VI$yneKd(=EL8emV(LzYlOC~@B}rVJ;m zPuZfFJ=LC(gSkf>1Y(+O;KMs|Q9)vnWE4X{Lz1a!$3kNc3%1NYV!heAi|v-`UN|tH zf!COnACJ(;CW8D#xNcww>57BoMQZ`%E~>is>2<*a^m=#t`Ey8$#6UWLbd+@A4#JWy zTVTkFGHoE@&QXJicU0V6Waw)x6^BF_b!wdjnhm`f*6F)k0Q&;Jp40)>&2O2U)GlRw|bP9LOpM;$2f8R3Q2js5Ri6Ep+(6(&k@CAFa;iRhJQ*! z>X!rH7H8j_50Rxy?Ra7t9NQ0zT0FnfmAMp6VoVvl%MKz`uP|^A)men`(bY!fMB8LDo45QumRhN>pp3WCts ze_y?;&?{1n_A?3FZZ9tO`XJnc=OFH9jkWI9DIXWJ@V=rYHfi}{Bh-!q_#0q zDT0ecxL3hL_W-oKB_z8@3&*xD1qa0_U^J|?5xeX|APahj`=L+vTl;_e{?)USx9Lb- zuzd<)@Gc%)CQHh*s*dzj*C`p7%%BsaZ@ltVE>=uM$n zvoeWe@Sp;dPoDauns=56=sEm~Jn6xuEW)}2qIlfH&xkB-s350HSm4b6ziZR=!}~_h zs3mtLbi6g){74-vY2O4m&e0Z}rmE+|!FFegZ;)o`ShB#DRq%6#w#F?+7oiW{ZCwq4 zh-co=9Yhq%(b3AVSS@WF>WP%I#Fzw*dq(z@?q~1H_Bc)6o`DDge$N)J&#=j<2 zy;dBxz( z;1_Bx&pxSdR!#O9Fr-kcf4Rx3Y}F{gwhIy8f^4C!?SkrVooQ~0`-u}943$~1CNKQ4 z9G&G0c{GRaOyE4;rT~KMDu=WtB}+c18UMH`(LUMSVB7z{y>IVs z<2dsD-=AVE=InqH2uaM#;V$=vqGX$ENz{^5oE(}58UO=wEC7Sd0DMHzIrl@{Pk2Ac z?Rs=~bx+S=0MfCOyEwbC2+UN!s;jH2t9}KSi_lVKKKyG=CKI~pwDJs9En3yUo#t#g z?ax(X#(t9uK2#zARJ5LI2(e~xD_Qt}qtDjQ37o317l+dk?2N&=FS zB(ch&F69IDQD{&hh@GwILsERuITRwl3^IOTDgn7ut?@M^RU9pJHXNC4fznA^d@Bo4 zs^BE00tPoJW;FGpm+FiJk~6wS!RnWMs?qIuRxO>igXrR^LzX&#UnBK7qBvCF-6fxfFleKCxByI0^n6#5)TXk3*)bY13PU`Ku9G6etABmLUEWOx`6ME(29wCJ-EJj4 zam(JtV;-7X9(7KYN__%;hVvM5B-K%@uWlfnEg@Jkj}UAbrLJ&VHs|&=3eH|OLXR6Z zniRnl(KC}nJIL@lBT@Tj_z`q&WOa2GEvKABx6tg5csAf?3J?ScGdh~h5_Co8EzDk` z??Ev&a}LKHXeR+;G))B025}5U-5${Q21&4XDVhk0@uYefh!}=a)9qx&ijLG26s7> zhvSBI(!1fx9=iL@sEpk9xh7SeE^oHaZ&sKg!kv#aM%=T?Xr=|+zqGkLg{UlhqcbZvV(prY!O+CITIjR& zN+brACbLU7b4k0hkcor!O_}5LtAuxeDz@TkH8=QQ%#{S}>JtAK%N6?o2zEdm5_!P$_H99OE;A%Su&*~7>)u%NIhhGu+cdeR0tR55- zweo>iY=w+~PdhmQpZY2WwekW!O)^8zT6Nx6hzy$jwM2$DHIkP_B~zHp3j{baa;|f0BdLN*M4lAx2@WAb=3uRSchbwu~;JSo0{(cF@RRh;o8PTrh zr_bvy+B$L`>oDA7(eiS)^`afUd3Str_WJ0>gRU7@8G%J3SDG<|!rRqKsPj0T-l}&3 za=?hAo!n>?@ZXAoec!cOXM^!t*<(;d$b~O>nS3A_5%51TifxumV$b@F$-=-!p^ljU z`^p<-j~ohK|2xGDiW92eqwZ#1t#?!7Qu+4uFK=S`i(+(JFG0!o!;|CFQ|D!f3>$h( z!sZhi2`-1!Efsg7%WZ;lzN?9Ya~3o*3StZ?Oz73pu3w}~p4yuHE3eG(aSYHRgBXMgD)mA{U=f=xd?*LmmmMv@W68kH|1R#0L`fzg7;ff8WA6~f} z4r`wF`BXm<*!1f$AKdm)QvSu79LPi6=l`}V!oIJE+k!<`NMN#&R&Zea&;q4i4q$g2 z=F;!1{Xm9aS3YpRAMRp zk`)GlBIWY9WI2m0QY+=|sa|x~6(-)A2A7GGA{*Dq$r^p2g}O1I5zayZ(k_I+o4UVx z?{Lh8i5QuoJ`=A_SVUHWnmf&6!=vQc6gP{F4Ky60>A2^ zyFXn7$7wqIWTn)#JB41}WMiDlL@H-zY|7y^Qe|19w-y0Oz@lb=BI~XNU@5z9aU8$r z`-fS2LvlJArwP*nfL|rc{6c2oBfOt1UHQE(m_vw8Fj}Nfu(!I*Bh_@^UUfOWM^J&U zVP3E}g_iU2;WhmG*ZCnWQ)1!-kDAo%InHF$&sO4}0@F0K@5)i!` z4c!;q<5(HMXD0`5`%0_oe|vEF-6w4Hb1d6w5j;R%jPqFi7o^D3Lm@<}l2t#wQ1dNa z9a40lV1*Dwqiew*YKMHU4vM76motKBRs04;qwtb{?+X(UpdOf)y~{kmoTL~Rpq^b$ zpKg7*$miMcDekBp3KRHoXZK|M;^=tP|H{5U9QH0}ORUf_SYG~3@ow_8yvlF-02(>G z%>Gw4+W*b(fAiIERgbn?>vG!f62DZN2v#~??RuF^uBZL1*x37`8HHl~Mur~HIS&Ec zT(sdFvnv8{Ix#m9hXHi%@2+TfvC`^B#dmrKg4_L&q}oIA_3J;qiH=ShVrJ=jYEe?g{bf+2_0j6&4H=5@wB`idVw&UN=HBWzp$O;c!8=%f+pcuOD+Yw zEI;S261G^km7OIy*^4*$iS`S$@{`HlF4#1xl^ty50VS~sd}(6hH%G4xD7)rhbV2{T zayHNtjHOr^g6yaZ;j44+WcS+{mR>7OcdF_ii=k4* z*ed3)J3$@{AZj`N_b*N~hLyN4u~WCb)|fEyWjl6vfB#!=S$KM!nlPLA94qXz9Qk5^z#!oVtoEwJ{u1*-mB>%#CiN33bl}L@ z24}~w4&T2#=)XEWJb3r&W&cbakgv{6Hu>y&NO{EIMbY&>KJ2{t?+?}BQ&lf;Cda~-w?Xug-2(0zd9?xR-uwe12_!f zMqt!1Klg9?{(h==dRho*9@2LINv(hVjYQ3B{@Ma=O}Kd-gkliDq{W?IZooD;J$A;yud*@)%o5XjXO zCH62$Lia#nd$zGf_7t{iv)_$e`+l(M^sOS{sBdjvXL*o1S_322Q~`_E?zbpe z;V>=#1IyNz3&+j74$}Kpbg1--d`b_LggTu}3Uy-NVLJOFopFU=Y{E&UbYDJFJNp*G z8}K4O87|1Abuf&&QJF`S+D+utU6(w3EzA+Jl z=PJjbv1=;h3e{W>ri!@>%WdIZIivBL=A9yLu(n02thQl_<;GU7W%TzzSGyc)C~Gk4 z`kCNU0zkRCV)XS@5&-7Iqu~yjkX$UFU=%7ZC~>7LooaFrgvk405*EO;0`*sc2cX`Qsx8CY7ggmK5IE1{b<|^WG%ODIsV}X4BCimd&^NWNf9hm8rV{_ zRrm+JR~%Wg?nbIn0I;*%PLGtCKv@qfYHqus&aCuk2ZnfCg@H9EfSO<3Cm{~iDIydh z+-2fH4_uN056zp}UKA9RzNStnR%>K)*$kk%oCE<8>lOeU#h(ceP7A}Dmt{dw6pA<_ z;B;nyFoPFmd(Nx!5EOOle3A4kRLOl?XiRQ9JAPoO4&VZ7oSt6|yI;jU3&<*W3j!*p zj&yaGQGJPKzwL?#w7FbpOkcUIJRhiyaL2>%LD!-nf@*XB)fq4*5s;4&#**oYa5>&k z0KaX=#2q6f?!ZpU!BwF>M6#3T&s_C7oHXpdKaM}~;6KNeemP86Uqu)`(uO;He|mQO zrhj<+=FP#|m(^@bUPZH_EjnFK9zyM^O>lqt6_;OlGY}?g4bRvKKz^AQX_*$+q`CCR zMLw-b#)hBE^pXpjN_NvEMt%8EnDfG;aQIIW>BZvY_^0 zF)JR%s*;EI(c5?L@hlcAB$_#Xe+It;f~IBX@Fdop!J=WD39XxIF-b;uyKT;UaC;?j zQhc=prY1Z^ao^V$;bYRbmDy6F^MMTivX8-}2*-%e%+ikfcUuU}>c{D=fU2ji>cebv z`om-nvE%!m*m8vpKV%b)Hg7MTUGJNNZg%ah(4g$k>$Lf`VD)QHEm;IbH&>~0;>o{t zFl)^uN}SL-q2?fgO0A)i>mL;S;mi<+Fu{6i&vK~LIXAG@iH9g>cvOka{-unnrcQ(o zjxDbBfp_f)?}XJw=70vJ=kX?uCOH-9;1mo5U&3o=DpFFUxcv1!gCiko>>gsy!{!a4 z&GV)n$5Dq6)3DSn?(OIk{rq{`AFem&r-y37$I>bJ3A*=LzaIw}O%TwhwZV#LfyQcV z7jR0OmD5C#QD#Mb42|C%Nv_0@HfO?0J~B>I|TGB9rh)zKi~ z`vv?PU!`{!vlX2kzdR;DEnBY2QL4f7)g|D&wg8!7M<`#QHpH}eJsnnl94OxS7`m|v zD|*;p5gJo!c;!o{P82v?Jk$&SUPP^50UAk>>Y;XY>cpGlm+xP{>c2ZUgX6Au^v&Dj zlUIiar>`7iuYthn`xlBvBLNB74ep|s*${m~-3SwR-2NMOYxz9pMYXH$#!#FV^e=+E zJ*1%7SmP^L=sx!j1c=gA_UB?PL9iEL6;UC{U;5a zFno`{s~i^4ZQ~b~RqD#NE=8f_mOWie0+prkSuC7k?RAk=r@9~9r5E61dI$CUN^?Jb zaC&%j zCh#+S6hC5op_(w<2o!Ay=-xG!1f(75cN&j!(E_iCey@i~K%M8?y-9wP76IfUw=s=Y z=Iouu#j2@Iw{n5U@moNxohzMH@z+EhUW$G9^oj9_`|_EpKvMCM)#V?@@!}@u8Wf-8 z7|xmA#b?}TpUTf6Z=wDK-vmAnzEKbJ1s@Vbdf~MYT)Fn}fzwXB-HHX^vlYet7~wdJ zL8i7tkxlL*Qj79vL?E-vth{3C5$ZweAH7DZb0vsoSbarws={BKFc|MOOs}05=2j-n zoc>Ih?#J(spe$8jExtl-7mf(tFW6EaA36aqaecDn<2zPH**7 zYVhH7Qzs8sooKuv-kDRht+#a{Yk;*_CcCc1kgRFjrah^*9p%qdS?fEd0|rRZ(xHdf*RwBgHKkO8fA ztYl+;gIIs1K}5%E{z00~Oqr=sI?U!^mR)Cwpup88mAZDRCO8G)0(*FEi3eY)CD1{} z=n}zj0_XtNo&+EN!j3|R76<~_tjr*aV$q8TafmHp@YS7DP~$K(l?6jmIgR z-P&wI7Ev7p>;kA7rc21VhzLpwmDvEXz0;@zeLxO)Kg({>sMu7$_ zP-H@JcU}}qWz1(IEGw*@QWFaAxwy6X%SiEZAKL7DWX%5OQUHd`X-+#J@rovSA_@`m z35H}i$p-`2KDcsBNVPf}^>K-y4;ZpqO8Io_K(>lr0H%9KmNytK3y>LVy>6&>iMyeu zKm=Y)5Uu|@&rE^Ia+xYY`4_eEK@4AF@y9DJIw%FyEc@$HmqjU;u-d>sIb6BmSU|ie z$$)F8eKcw%q-_+u3#cW^WhK33ZlZlkLa_JH6 zCr4va58kkknguxkMWq+LQ{1_nsw4RhWv&B>qxu2EQIn~9R!$Se0_hCuW5_jC8zj#^ z0)7zg@FKg!BNLpaBxrOxa|dcAEa`yjI`uRZm9&JSS#-pdO4h?Hg42m{g!HaQ*1)P& zpiW2vHhpbGO95^+Xc zYgV%vlFnTevX;Jq1DdJc?guti??5Gf1_^(u2+tW(*>Ai zJOyVIEjLWo@Z2VSq}k*d|=?5 z1&0BN<{jpS`V&jOG+q_6Icc-=Yz(c>%Dcy?tJ#Vk6qvv2wW0b6z{gcHO>o^=wwiR*M5`9tz~5*$@#& zJn`w!F>Ra1@R2`Gie-POhLv`v$!)3j92K{y9wpgq*w>32hU@M|BThw!eT6w8_?Ke7 z@=odg(>(+_t-sE2&YGj?l$2>Wecu)NB3F7k=bqI6CQc@0(7It}P;?Wow;f@@eN~;8 zSaZd2QKV^`_XA`ZlF84|nKn)CuzD}O3NCsuYB9))+1@}8 za{8U;?~1yuZ|oJ-8+T*DqKNC_2>d;LpK82{=^4o-fs%XkT~AfFCO> zwG%7j=IJ0Q)c^lY?pg8ji{*mrYSIzc*@iF<_D_*Xf_;;M&z@ND!kS@(dUaeeG+4Pr z^j>gzs!~mpJFn!QbV>^A#3y74X`73U?yL4XTMh7o{^P)@C@qvM87bp5**77>^%&*$ z7^~~JK7E54RYgE)vMt3@t424Sxq9&;FH;UiBdh)5a$^=dZ%qt3CeKm{+^x8ngNpuL zJJL#d%RswV))~UQX(!J_eZ3g=LQCl;`l#j0hW1=ru>c{(n3w{fVj*t7qI=`eG5$$A zWl$0&r5?We_fq`|Ru*^^sE#OAGp;yvbbZaC6l+?7INP2X7HXGg!_00^@q7@n!c)0q z;1;~=Zu9zyqH#O2*LV&;g!=L!`go(Lfd?n(T?BqX3N>$I28-o`lAZ94j&069b9$*3 zatX}EG4CEx`Do8MamX!1aMyTYBsNcPUgV0I%RUa#VGrq^s!W#KcC}~T^NxdBz6o=O zml@MUkxckHgwVa!AMELc+a0*DFeO~&0KZkcDkY#SAf)dkfet35mAQ4PF=24taz=@z z1Se^RC6$T7s3Rn+SXnhml|!dw9V6=vmxWB7qb}n0r#;g3wUW~JC>Th}c-4bZcAbsT zhv(6`&JyrK0wfDKi$8o2JLAI#boO`U2xz<>Aeu*0w%zOZGf+bNFh7{nrrOv!QlwEZ zc4#?ZC9;`M21_IC{Yh69vtUOwb_m8)GQ3NfLK#NTJ>kt}?lSXX7CY(?3sfhCwnNQ~46u&%iU~>V zuVwnzB?yMe9#4{T@wj%Hs!N2^w!xGf5DSCv;$jTC zJH5E>ip(Ak^X4j_upHz!{29@!0TYjvB8&{?>!Hqare}3qZ=-2asaH<%B#&_dYNwHB zO;EeAL+31Xx&c5= z;DRgKa%E4GWOWJ?5aitL;)yJ4iVSo#3$Q=NC!%H&mT3*Xz&6(xd{L|^o6AFjs$?he z7u5X|{ISG%KMa#fiaR)p=W~o#!p&@>6TuT#3{hu|I%)5+7g>N%@<423Tn{M+`PgxS zfX5dCS-544G^7bnP*YZd2~quY3q5}D(1a9;SJY}O3pg^Mm3cnLz!E44nw84PIbAGA zS&s4s;x|&I8LO#JC!-SW1@#`pC?dh}a)MY87z$JlQ#Aou&c?2f*)|n;+prvcC8j_$ z_o2J3@+vt~?KK}6U3r5nJV=@*l_$UCs@3La2t%J!&I#}X^8(gkf>;X&<_SLN&($OW z``hNind{EVX46-<{)6X|(nWZSuIoVbls@emcz5`%M)FLwO+lLr+(`>>XOYDLN+$e< zad8#Jlrp_A@c(i+jMgfL^!(Yg-~4XZze3Y%0XsdO-ozR@*X&^*&b3yS0S=tA+5MY7 zZ=N2?+v-ixn{-h?F3!i&JX5Oe`PU`Ho5G4{afpnmcKZ-+3NV8aHe8XQ_h+;Ow`QWG z-Cc>`!=ji7it4f)7G?>vutsv6T`%gD8*v0t@hme@P2T0=O3Z! ztgGfc+$?RR-Y8=WLqKqJIay?wIb)Es-3@P#9*_=r>I48vD6GT-PIX!cL{;kEbtBNP zE(qq{5!H=>g%8p0g=yPs*ED6^56w8}7+umNDpyD6QSusK%prV!o-aB!+%ph_hawf7 zL@$f;~+neaoQgRoLOdBj(us^ zYrKi36ssZ-3Hb1_g`xOV4(~L%1>in$&l@LhSkBAChpR$#2Ez;*0U%0Va4c%a1dn>& zWlXQc&{LgUJJVNV+7XN{@xAEj*CX}+r}ct*vtU-yA3QgCf$@F;Q-E~IDfksu{Sfzv zQlOf>^j=vT;TyNfDx|hF9EC0~sUH}omIU*`N1{2|PWF{4KA(Pul-NyP6 z1W$t5U2xr^)B{X?&2_3RG~*{Wiu+49Yg@J58+wngS^a>gexiC%O-iA_G9#eKnEEcg z>%;?iBL;Uc#y0O)!`izLnaMX8`e*x%w`!`>osKyAbc%<#Oe%qGYa;{{8S`*B& zw5GI{^OAQv^-Z^|03vGY99f6I|rymJ-h`JJoV)ZDQ)pyDa56O$#O1cY;_D|Q7m67o2)L7FXMiM;dD9}s8>u+NWRaq(?6BgJ^c)Q-g)R@M;oHuVca2FgGpS|nA0 z#D6};9Ov@s^I!kwD}P_0^;D}B!3V%fN#jB=7o%8iM^CjjR(VG(r#y=tNFH6c)Pv%Y zl}k56d*u#SQRKyt*8$a`d46f@`9K-aUUY)R$tcRiM-l}&KzSb-nA^9Zc@;|sP*i2_ zGS8JUi7TjjvYbBM`f`!av!PSg^T(atlktnA<5B;s3Pmo@LirnN1~g)OTlk*(Dm(B| zls5&BUS}E2E@o&a)Mu6SFj-eT!tENwE;3)Jr)VV9LvuBjqpcn}^_W*6pjw0VZp70( z`J25QYdp-Cuio{)JvjUhEG-=TX7nm)U;f9-|KaJ`_K%D3wX+3{1^;^6%V*b@@Z?F9 zO;JZL7bBB&4kdU2MhKw2krA)vhRrb`HAww%%Y32m`UL_yK6W&->nAP%$=!D=4y;h3eu2fE$I)Mxxsn8co}poH=|69E5n8+)QU48{ zz!*TyeT9G^oUFuveI<#yLJ4E6lUw!G=~p46Y1)N6b;PXgyU0Z zo5KB7ZCS!?-_SC2iWJg&Y~l(Bo>IXmEygT{Te(R5%$LN?Q}GRyuqcO5tRysL%yz|F^g1mDd6|09MA+PP zgmFl#i(S})8Fc#}SIb{`lFSsaa5$w}0>ynzl-0%h8HWbC<&(SDvT{C2?u2&qSvyWY z@oxG2Ne{32E~o{jLRG&OdtFz^Rx}`P8T4Ex8MyW_I(S%Zufb|a@JdWx92&U-%GHYJ zOgx6TZwg~S1d1k#M09x@B5bZUk*^S$vvd3 zx2$ofS0}bcq#94mu5`6o0y+VA2DXy$o5BTa^j&&~HrLw^^sncViq{!mVprbA!(Wd91#T<|y7~IBi4P%gYA+2t4 zRit29{g{h7G6-KP2;8W}0gyZmojbYfDW*v08b>dmpvHr!9Lw1vn=rPcATi8m>ak2Q zZR(_I`1f{~!(klee>>&%)d4jwcZ8ItJ;YOZs$S`-zp4r@JPxD4=`X41Zf$Fw!O-iPjayo1-!J7e-dl=KIs zeeQ6;&kneF;fF!CC~<+*x?comK3qFWcV278Pn>>Kdl{@N6?{nW*?vUZ@^mpenvHXO zs~g!RK6lpGy9&^A8R3+e$+)OK#dp#membZYDJ7)qcLp*w`=TJh_QkpaCwNajEw3MH z!$Bvr@v!AK8~j*(Fq`RT-ZAJ9AA0BUpAOF;8SDs-|HT)@MHKV2A`4}=w*i`b z3q_>(O0){`}(I-^74{=<}k;hiP{`MO*i!XhRRlOt9`x+SQH)p zruyhlC1gYw{#E78(^;<0(j4_V6g^LmcjK;{{C_>k@(+_4;Q%uX!L#vqW2wKO!bCa@ zfJxYx1!3uAqtDTuTIg74;q9%D4Kzq&pzKw*m`G3vR0)(SpzuCP-@K!CHy8ACqa@u> z5$@|*3o>{E0|3tA!W6?r)DI$p^8EP)_#C{89)9ynaFoQp1I*Gj|E?6`;wkCar<)>E zbacc;givd3UI82GqIm|u?L;+Q=)^GhAI=ZxY~yuNlM%d%Vf!4{lP@mJ`zvmOyk`w7 zhx^ekld?9?g6o5~xUTfgGPj~LR&kAic~$RvKdOx!L$qbXGoz8ePvtG#%H4>b+kVsj z^D=g7^19r8X|DC;%f2G>ua&UnOr*z&+tTb7ouSo1aS(txcHI|TCX)Vl58>dKB?OmZ zx;;atM+-;_A8_p{06dvwhyV|PD>ZU%Ot-p$`DYHr2N>}e1yk(vFb1(wG4gqb;efy3 zPIILzsuTlBC23ZSqE2#|WV7uap&MLoSigTHFSwPn0^M zilZ0}xB*8%Vrc+Irs3KI1>rP6^^Y8C7~n(}Xvg2Aj5I@)336@=gTuf$&t@3zgVkmb z@l~|~J^_RlpwKZ>Vg$8YwPXo84i8?B8_0Xtk z%@uzq5mdP0%4-Ib32*{tRCycY@-X1fcI?KWXie^%a?gHYIeC4*xyLM66YFSfDp#=< zLyK{fx`i?_z-!TnHW42NYGp8KOVCqHO%)c{?Gur&djP@5P?sU>@G+6=Uy2AyP!TpO zN#}p#Jk}_ALE=t8+D<5D&BTIvalTkD72Uc3r-j%gz2TJ-COVo6!RzVYr7>mjON5rv zKUu0F{CY6RF;twL^qVQWiol+$OL!FzF6v#XTJ>;H9DHR2_hnDy8BViXB~E(R2vkur zPz4zU61+k}-!*vm?Y%z z$*V)!D^&)3u7tWNrXCAO`G%akvivxx0_d=_SdlgPHK3E>`gGn$JywOywr zJ?jAhe>r?8`EE;Jk`$L=A2nj!Eo0(fDQ~dGByf5rPi6aj=Q+h|UBzxRjsqqK*1a#9 zUa?lIv?85{2ie6s89pf?>=p}79s(@_2EEQd5=t+GL=qzko^phT|*=+JLgJZ7EUIibuLvjYJrZC#Ls^2|X1aeqca>XXNF!grSgFuwn!^SI}fW`(2x>=-i znKaW{n6+N7ccJ7>$0q*zc-sVjA@X;DsCNfx5^h5wfiq>sE>r&n>{_cJms%>-JsLt~ z3wGDxM4iIs1bT+5IPl@eR2-AZA@G3?vbU}lud_h4ggT(iJ(`6oqixq_{T6?1acX##3Wjs1*6 z_0`CS8L5;WYA>xyQ|zq2=sa>kdRshWR;ns|+Yt@Hd)1>fV71~9N<)&}_DcY>RO7r(D;JDwPf`Nf za#c1_4OFl`Mrv_%JdR+)A(ckGQ$x#!eR>@0o6{R*rz+^h+bmT(sL=1rimN=ZzL>Jg z8+D}h64bo5;;ofVdE-_U?}^azyJ<~1T2E$G8LIUe#BC_u`t;k=zr2ZSxFSYEqTt zT*aKjA}>omX*?$N1Ex-nPfszja|d7AxysAM{`%!Z{Q?caApp&tDalLSU=M}2w|bK_@n)^+p>9eM zXqZRKW81zn(?Yu=q8A>+fD($k`t&9no2nB9-WQKXY9FQ3Y_XFXqHZLsbTWFg1^GvI z#{4wJBT$Z!MfvFHnhF8QTur9Zk-+alGz}a~1=N}s(z5nL$5bl%d1~H;5#ER(< z-p8$n#_zEh_W&h)QB@JcZXf1Z+jf<1o7JznrFdQ1ID+wtVme|_?rTmh7x$+YK~ois z#8w(uQy^F9y*JpWfL_(GaY}qs564cXuP;#FYcQ@>5C>hZp4|_WmOu}(9*}YH%)6t* zHrs!m4QmLiExz;pq`On*s3Vzu4f{u(Bg@-vw8J8qt{-zSK%zf~9E0tq-lCYnaiAnL zHQj#F@NpVW(A%vy36{t+=P#*eCTtv>(P3w~egtZoK>VqBX_OQ<>MY@}%YTZkM60aJ zRpKFYt(FNaM2tYg*EB3xYuWgV5{-3{=O>V0HcbgaHGV()I0JkgavHFI6RWJm6Pg2os#5iMhOJUA2c))RIlqR( zxt*@zLm|8eSmwoYrk+)NjRc1Xq&L9$Gz1;1UAeAt8DXXGX;xk?vr#(o+OXobgQ0*) z8J)g-y}OMa-kIL;mZ3^X8F^d?7}o9WG=z}cWbpWVYzk%MYUJnhk)q{^HMIdX@p zzTpD!r1vd(h^|qmJABRT!7q2_Seuq-B9L@T!U*3C%bVaWcS9#AgA2NV3|;j~;d>9- z6J5ek6io@>Y{7>a78~(Tcf8;A_)QlvCB6>mxN98qU7@tPp;Wpis&m{lCMAqgJXk5& z1T{p;jWO}cGCjjq?x1vxAs1{$Uz@CW|6;kIC$5x=w+rq=r5{gA+45s`W@vw%A~K}I zg#(gm8pR6KH_08*#=IT66W#8AqFIM1>0%T_potOon0gyCd7M(8g`H`-AUr*5i;d%6p#a|~0q*ob)$Ifeu zmJTxnxp?P@HC$GflxHDCE;SNQ+^%Vd5qWKq%xCE3->*0ayW?+r_P?W zYONpV^8#{tK|RPmf(MRoD6lLEPAq7mz%%H+(C#*qJPHn3 zwYCXlxCR=f7znHzBQ!mTm!KN=Rr!T&@^Mf8;|9A)TYEhG`@?E_>(lj_DiN^29oCE4Fe=Q7z;S{ht z%r6=j>RjkGx`A%QPAU7epvoDiqPMbZvbiVwm2#Uk6mdtm5M3TSdI(=K9KEjCt7)r} zQR@J2tzWIQ4dH#~&xRtOHIQ69e-05@u-sc$90Y^IqCgfI)>E$#L>Mou;VB>U_Z^KN zL4PG5NEqM7cyH@8MXqr#ZO9TR5F}-@IJ32{EVHqT%ObXRB`As-MFqJ;C=gUF43Hke zQE*uS1~Z+4BwBc6md@=UXdM~XPrgcrAGxGmLWM=~3UHL)DEX!uoJNc(S9&BtVZr91 z&0cg2h!?siaE+vB4ziIF*Z?PtWtq?eGVn7&b@(Z~iopy3qnZ@?ayIhP-BDQjx$koF z?xhAa#4WZ6t8VCi17Sm{V;RthdisiVlDtoI2pKKz9S_U5AR9Nia@x(+iqWk@WV^B< zZEpkTcgw?jxe$*1;89-J$Clf;?sY1Q1J^#A7M&MC>#Imm_Je<{TSl~J33x)Jp9FVu6zpAAGX3UQcWJiBM=ZW>$5A^UL50thPEn{mT_Tql;b+JH0_ZK0j5 z=LT*2lN9lf%LxgF;c?WEANOAqOyqe|T5FV|6{Sudy?J+haz?@S!RUWvx~5Zm z<4SP6qF8%%-&tR!H}_v3z5VXhiK(_Gd@+Y6nL3@$7k4&hgq2jXU~=7&ZF>y8Gt21= z$IFv6Rg&Z1ZFnGW2&Jmmw+*v5%QYO_EI4mi)U~!d~ zX+7SQ%WxuKw~5(mHY-`w)upikc1{)CxA~3DbRCUWt4p35DQTT5HY_Nve}~>sa|KAL zN`qD3M>Z?2G*_=6`%#n00liBQd;L;r#xG6|PX5w=cX0MC@}^!sX}Lr%xeO>c0!04p z3O=s5CwvyJ-G!>B#1A=x`?AI;V$o?C)d=^{+!CbB)6V4q9e|)H3x&=O03gV9%x-+_120`4@+`&+_Wkb)!)j$B_rd1+7xOSD|*)#LT>Xo2!c*s~5wv9d-` zVP=S9@{|)k?+RKs80n26RI*cd8d0I=$^W`L@v068CW=xQX8CM~Nh30}kr0TxYZ-<% zlKM-wv9_pd>aDm9Z~_6n(@FE*8qv0rT)p_wl>J1Lua&71C(A`XO)j%x91&#KiyUsH zVOG95Q+7iyIsk}18==>Up_)}PD&tCd&H2YmWoFLC*=2-~+Gt_n!<5wlg}T+y5Qh50 zA%3;@p^en^W+9wC2uTZe`<7%TL*JF_MuMgcZF`kHD z6S$zH9?eEZoE9nC^B_@?;?8I}hrKdaCtg}8d+o!{>u6{40fcR}?C+L|bG%gamIE+x zpW?ME@(TU)sXF|YY5DZ|@1K8Fp`fRiqsjZ+uIf*Jg3BReA4HW9JMj=^UJZj7c{Ag( zc`&}otr@UdoCTI*)NU2@r^foM9cC>dwX6c8obJ4;q7}I9{VcmxLg-Fu9vq~D_LMr9 zOAw$9Exg2DA)}C3rrCX}cYdLmdl>EPD64*^`@rIE2ZJJ}Ni{CSl4sx_Q1EU4(z2%{ z+Dutj3l483JWeqRNJUG=N|U8fp@tT)st?3LLLC4~o~)Uv`e~p#x^#yPnM(bF@OClI zQ=~D(Mu^v<)qPoyRwq9kkWZva{ogu>?bmLEROlm_F8q2;+-k)#_do6SG86Gt@%0QB zVswL0%#Tfm8Y7|fd=1llu^f*X+=hvb70<#LvDP{$+2kAYC-LN zplun@sd^@3ne|rexxZPrqQ_=v*d|yRkX#5puaq`Pnrm(n%ugy=f87bSMDSgfANC8_vP2+%9x~TWK-X1n*LdhH_ z15tpg3QBqi=-WqO~ zIqI-ow=n%Iy-o|EwI;JWbe{$dQ@yY`9YjmI;@9T}-s^Qm@R;M<+jssFe%Z-HV-BA`7Gv&4k1YqNKWf(7ErDjxFf~=j z11ENEHey`eMN)zUsg?{?i`=>G!Q@c-KyRAFv}m`OEu|YU`6?@1wiHD0U8=0QL4UHy zV}bIai$j{PuA7a9AN}@MMueH0**iRrL z6F7&3=5RxlPVFLSE4!TK>|S6RrU)WQ7K4V2+fSQ{T(tttnX{wlaQCZQFHOO9 zT$$jvW;6+Zi52xv@o@Lqvu6k>{X720B8xv)Gdjhr!;5d|pNCg@HVmPJpr8$8W|?B< z7WKInF+n#C=OpbVGq-?D3{CVfORLV@V(e-i8vn^6XMc7p7OX!d&R|2alAG3bq0=T! ziiwoH*7YnQ)60lr|2^sckMyVeW3Ta+;CRA56N__AEbNf4h1m@RgnG|H#6-UqmseK3 z5gk>N2W^ z1^D5cm2K0s5`qy8<}#|PlT6tsEkd5vS*=>Ng+Qt`MUhk&@md$jrRP4>?f4=Rz7E0K zQgdB;z+DbC-~HP6R>r(W4U~gPlsRO}K;Ui1Y1m^C%9t@qQk9I8=-kwpVtfpIg?U`v z;@f<2r0a{NBTUDsQpI#lyzcHiVws?!RluknL4M=}Y%q-gMP0CWNd~c{_bAwl?w6634@ zJGl@>s`n}2Hz;+kAx%Wa(R^=e#bVt~rCJl#J_qmd4Jfs3UPId^SKQRmHR$7;L5w+Z zIsEMb1!u#Lle9NlPUmn*lDiw51-Z)|!BS}XwP47AK)xK#sO)>@t~m^Tz7L0 z!+O(RYg+G7I)S;Pa9;SL2mgbexoB?ETmP5pXT08kXGxi_UvNWDrfCmHN&CqfC2c2* zss5Ccr8;xb++_V^jgr-q#q>OQZY8PN6qUd5LiBi$XmL0pp0y-ns5wCzECBY25rRh4oQ3 z1iZ*!Z}%Wl7j!ir^PYmhDf*(6IJ4SsVc}`g#SMgC7fZ%L16~o-Y<=Y-C6I57awg|l zPc(Kzf(-A%t-D)LO2%Wbh6kxSd$SxJ185|ZA3PD4n_A&SouC7g7dk&j8uKY2pH}X+ zQR{=TQWEC$5MM-{0b<6e^5{GneoQVMZm->(txquQ#GzU=Nh0rA5 z`(!^F$2*_Qlb>DSZA)C+!0Kt|aTz%?$qR6iHC;#>>l=E2(h3ov<3V4)9&dZRs0QLh zE{F$P`3T7NjwvtD4zGF#s|l0It{4Pl?etEm1A)ml0Wnan4PL|xP$D4AJ`azQN^N$U z9j$U#$31u1x9L*uhl*^zUn_mTl~v+V^3b}{k<(0J4~WneaY(2idLPp}0O5=NV;^D# zpm+X>^pk3V&>m05#&WkRRxSIq7Kvh_e-4Q#)~Y6EpnAqb)m)y!i%o0_^&%l7mq|k$fva69Iv!l|N2PBz2Iyi&2)F90eEB7e4j!RkXJvXycv-15(FQFZ%GJqibQJmKv^rYhsn$)P*Cg zyoVugZW0tFl0juiowxyzAq>@)6_IwrFLFVR03AxM-;#s!B>Ks29;1k8Y53CiH$lA5JLU97G}u`A{2cSq&QWx zOT0$e$ke@3HVz;92iWE{KVB@1FX`@uYn6?8-W00pOufBQp z_SMP3*{lAG_eZZmT)XLb51_~s+aBpyEy@;JvP2!EQF=@9#S_E3U7gQK&j70?g@%0~ z_29Bd=2r-`eU;xx#4wb0H;{LSQ$7NDsRbM1Y@eoSNL1mRkX=px*0O4M_YZ%N99$O^ zUEM2f7lrnqCbN%Miv=KSmPJ~_BdZSoP1$6PUILqnIf~b?DHm>H zRUgEKlflu?MvjL~XLM9^qv)F>N$#bhCd@PpgXUdaZMG+TPua*3z~G>RM}lco<#G;j zk-eJ17>X8&?F)|rl}Lpy7;E>7FTQ{YLZ45cD^?bYXK0_YkE(Q8$Tv!PwVFj~?NFp+D+m=6A?yWY8O1q$PR_ za1$(2;5f@>keXPS^BYMK23mfD?<+oEVlIU`d`8P5pP8KT`T?N|QmPc57BqiH3qUde zl(1;FoDL`iRcVLIsRo{Xc^Q=zM*Aa&~g? zwtw{Y@b&wbuK-IA${+NOzIl6m^6K#5^cC$A>~;9n_owx*>Ar^FJbnM-&GF0N%lNi> zwNK}lQC8JfP=CC`+_hpzjbKiL`m2X|4T?c?;E4S=DTi66gcZg`mHRQ}WK*)k*mY-3 zIJtT3ZvQQtkd6Itb>G==3W+CB0uLs(hkrrVY_&|ddA1q#b2iivqiQrzMtru2o|>0? zmqosucb-?>3R}_JKXC?omgC2;Z8bXLRl#jQ;1^go1(dUqWhwdd64@6U^BX;E&kR0xS)Mc!+%M>Li#552cESRO4}|b`y<; znwfwJ0B9RILVnhe0hIv?7GquFJoC|S^{19Q)rPjB63W&*CH6dLp1tUeorom;(O0qb zI`|&3YG#8Qlb^(m@FhuRQoqzFEFh3z?pilyC8<__FdG!%wTvvTB~m*hm};(j3%NKr zFgPTMw9<%ou=FAZC-$?;IdNNR1_>aERfam={B}~ zyWY-CNGQXHe$uj8>m0S>89p#q;;lw04IesHOG7>^tb3)h8W){-FAm=+aC{e()@6bO zJo3C7?SAfhg_7haHlvTg)23$b&g5P(h8WcDEaO@Vo|XM?D?IQ(47bvx;4>yLwgokO-OF;$QpE`4%SBaUxzTqFm~Al8R@J650j5RWgg; zbKiLY=>?x!2?n2aq!@f^B^eQ`jA0|0wz8phM4P*C;k92+%CUFAy!6HY15ir?1QY-O z00;mjtqNV1n{v750RRBk0{{Ra0001OWpi(CVs&Y3WG`Z2V{2t@WG`-SZe?O&V{2t@ zWG--dty4{F+%OQm`&T^3!Cr_>*yBP9P1BY_n-;pzLQ;$@k8KfIGSYav+h1SFYj0e7 z>7fE+YrfulGvh0K{c`_3|EMGxPx*(M46817# z2%pGXgPIPc?a+rjFo`}%8fSZyWi#^Vs4SuS!8(R8#9{Ht&YA&QM zws@RSp8jq{mVP7D7GV@PV}QdbqiW2p2iuEm{8ZfGDP36HV(&(JO4H4nz)Jpq zbsz(QgV3rDG+uxv3TqB=K1DSuT^B8J$)R7wKHK~wqlY!PoL2Rv3Fl&Pqb7fiYGnA% zj=DxC4unQ19R@|h>G0bWtJcINEy9zf@6V;;viq>5nxKqlp>-ZM_p|`7QYSf{oC?Z+ zQOV?^m_OapQD9fjmC9PMD4W0~2L1+6O9KQH00008041#oUGT*nNEmeh008^}03!eZ z0BvP+Z*F3BX>4RKVqs%zWo~3Ic5^Z?F)(6bV{2t@WG--d?7i!D+sM%<`hT7Rkun=( zhJtL*C5h%!5{)c-v?5zxmOL}(le8F!1SO0~f*$}%Rvhp5Ij?b^?9{EVXn>ULnPg{& zD;ZNnqq@7gy1VYR8J)a(`qS1?KFa1*wsknp=5;>FvvMceTco4wZ0kw#=(EjezgXOr z`PFqDJ$dx_t1bA?H__>J7QM)-VjhjNn`~MvX7F-UXX(s&;s5)8;hiU6!aGmCiVo*> zHid`fqA1fkFXo?pHYtl)G#pNr^|H){!ziD@vpP!W^P;9E)o1e42wuG+e#`g;T97L~R%;wvV`Fvd5R``PgQ)c}3sF+T(5lu%y*e{lVjZ!d>sGl#>(c5f3 zuH!PRRLmCnG%FhrX1G`|1^@R&SzMLrOt)9<)brkF5qzEj%mFX> zcgw4TccW~9(~+O7zsqbfO-I=p^q_1A@mx99>!>Bp|PRp6y>3KzyI| zRp-URLDDh(`Pb}DJR`D4{lWfU4_`jtp=r_!y!gZHT@Bm99?t3ABHNqhY4w?DoFr*^HO;HqEG#d;UU^m7 z&Gf>UZ|aq!bbhr=uQCSvsw{p6Vu^Nfh|9$^JFDu_CN}!hLEwd6qoe$7#&5z{e@M$- ztbUCFJ3w>eUHl>X2SVx7Yr`K-jt)-&6Q@7zm?;8~aXzZKZ||EL?;yg7Km zn?&idF2-366Byo9Ls|D8tqVX;ctz9c8U2!chyT6kM_Yf=l%IUZUwAZ*RC077? ze$$J8$g5>KJ*k)Dy!e3|zyx0w^Q^Dl%jZ>{PNzVIrT6a1;mecLy`!V2hp(NNfGXhF z@!sWe;HOOUD)R`w7sE!Lj_Ey~cEm%Acx*d1eMUEFnWvY4etQT}UeW7SR{!RF9_#Lc zpLnm#qPt=lmCN}Yk0{!%Lbq@vJDo>Q`Do5cq{>-3A0u(i7WJQFp73TAz$1$KPr*MxFvg#E0x)HF>Q89_A)2M(kmiZX)Mer77c9R!N z_w+H>z;jtww|RZt1CVil$63`;0pjU=nVEedJs=l~ z=s$b1bJ5pNH>16(f++(kfg`D}b3zRu5gC(9_%dD27TGw$?uaM~_zy7U%=hGs^U4C3 z&NUL@ag|Lc_8j6%+ATCDcie)CBwZ|UivSEW$ZT3=!Pe0iyM*32`XZVT@+KNQBUJXE zv}dxs%H|n}*4gk1gxMjU8$*;_d-$InWch*d2TmB}2$TtkEC(dq4ngY4s0ooR{JUfK z0)O{VKu+*KU-Q?OdoK=7UhVB44Bxyy0wSOjYU`%j!ZB-@@IBtaUl0WT+VA(1C7`10 z^?A@DF3j>#SyaWO9$t;#_CSiPaXXqJU4p>xjH%2L*sY`MUKu}6LEgVQPkK+Y>TO*t z{_)dg@h-vt9M3cU;{gl9}g;gaaMqAJSnsPS>nF&!2k-d z(N%tv&7nsUq~S$DO9DUr2s`zHC~kYEG_S1*xREN1&th)9;u98|1)%k05|Rx!G({-_gz-2z^1{)iv>PK-ps$(UyHI2d8@W=mP$SD-p&7 z-h*=DnsX;5brVuic^CCA?;>1pRL^A>tUu!KeZ~l~2Wd{P)u=YZiEjb49&komumNB( zDwweBs)63EcqhT1>3Ezo#cE=SL><`&D7@2zRn!|4j>$S(uS3ztnc9M$i3Y=z*U zTN~wnWZ&guy0!25vOa`lPwg1J=)K^``SVokEZy&xN>%;m;zG_QKv_i$Q#Hi1b_h%g zqYM^iNEF9hQb;pe2jRjNfB!*W`Wgfhj?zY>UJ{QFkTjtGbUCd}SF|~L);&H-7icMw z(U9N)U6|E7)308f>D9Q1a8FeLS+2hLMD`I8w2(Wo5=fFmI*oh9)3~U zcWlw8>H5FYteX=^VC=K1*T>U=ApT~zK@2#B<2l^S^DHBQEuW9`n|usM9sIZ`7RxDK zpP_w0_kwJ{VnQO3KxY&rMxNjsWXR*DnX+boz78T>W(($`F@)S?K3|u*8q~htduIk! zn&ut~!J;An=1BMn+C@sZWQOw@$Vck@(8%iBSHXJye)idDnpTysN$~6eun7n>8g^h% zF)6Pe#h zx=DmLB$Jb6sfbZ~l6?8-%g29A9>?x0)pXu${y2H`DA3Nbn~~TFO?lStAViWGl1((L zm86IFDZE10s`G+lRS7}7Wn1P@ju73!jZ(&}=%89&j`OmTlhT;3+(S|zTzC{q5SX(V zs}YQ|MP;A6#_$w)S1m0((qG~HIJtved^al;ZzD_)vO9F!;(ZL4K9V)A&sJ66!KpZd zV>90Z=3~1n!Zq^RnVnfUC3o!7DcVzScS}9fRX~# zbvPeq@A?5W=Qf(n0$06$6WN>ONum@D3hQH~Cedcy(XuSbfGnasBJw6r=^0BxIKu|` z98t{1Z?mfrPhphGpaJ1|zX7YAXq94o>O;DxgYyj!U1-mowToHk{_FBS+4?HlHzmfG1VKXZb~Q zzch&^+)>;#UGX%8(G9GW%pG@>KH}81@2%h($7{bicI39ywzc;n-5I>;&3SoRF0&R8 zUsy0$48VH?9%&L)H;JM&5-y~pstNTntuiI_y1PlduzCh}&9`OAsv#gQyk&5FJwb2# z`zZ3RrjH2=!K8k3y1r-t4znGIKJ{MX6+yz;yF@(hdK-T^eEI6l>0dSm76JA3y2QN>D-aPb>`wsysz+N98 zpMGkrbi(8lW(CN5&nG@VCrfg?5*M4%-t;!Tt4I@@MzlAesXx_cO&gKD-XsJOJ7zli5lDDkoe%l&mK1?qw|7>1`t9$S^_!-T{gJ`HWS6U58 z%0#YV2#==uJnIFK4-LYKS7(#*Ijj8^^a0!tzk6GhZ(+2%YVP4yqTAb5`&%^r=Af1a zM%y2T1M81QnXJsyY5w+#M-^acIWq|eK0xCpRH zpLmf0HmVzt_3WuG2e5=FausiP!&mEwXwT)kgd2km#4-wP0oYH6%-rsFU5hRxfR0eu zUo`F~HCv?PD7G;0Kg*(~JApSz56~HyT?p_o-wqbwZw0WTn)Tr9X>-ltyagaGY)CC3 z?7z8mM?Bdoq@B2@%Arlo;6Nt9o>gI5k7cQ}Z0(leAlpdU2RDSZKSMw_r>3pdIrs42DJ@gbPPJ??LV{MGc+f9u=T*N+jWx&Sw+`8)8vG7 zmC}KBE%rKBCmT>8ZASao+2}1TTu-zL)4P7OoM-RQgqe-i5Xe~z@6!>mf0dYc@q^ZM z8KpBW%iqrq5$VN0qPqdf>D&kTyxml``<+4yD`wAQHHKZ1F3w|1dL+8>bfxC$HCmTv z92=qTPW!l)M^%CilhI;1oTRgSdS}nhfdN$gJ|=&m_}w4A8N#;@?zA_fbT25MDhXZs+Scf(}v+wc+i(eqlrN#}M|&eKW6?3v?ZZz3)2X8Xr9L-?PUf*D8LP z`B`Vn@pT#Ab1syR+;o;6q?>@fvEPS+o8Uuq9GKQ8B4mxskE7(~H112!EfCtM+QCQ% zSvSG{qQvbC%cpMg*a$3!_TWI{Ht-4=aXvTcbeWMNNrx}?Vhty@_~f(#8K<_55V{%J zM?sp1@uHsHE$%!6tvxQwkgnRG8rp#7(r#W=fMUVY^7 z2}bw0smXviu8t9_!O*{EjCFbEQExg2!Q}5BO)x_RDE1_)<^)tr797HJ$Y@VQ0Q;aU zi?Vgh7IV**d}})L)l6QwGK5ssYf8#|jwv?^0`vulngH42`S7vWIb5+p+2bw$HIGw7 z2Y@=OYHZmHJd@y#w#Q`#|8055L#T@BlFk|%83R4!w$}(S^gbyp$gZbNT`Wh}(vU(e z>vXnQiB~zi{!(H5{AiZd*TvWpKWYI;z;L)d{cgbfA3q__0Ot=4U|tHy4;tS4K4@FP!}P8S^Rsx$}2 zS;N8Hb&8=uFd5q6sI-ZHIN5*kbh!8W`SA4lDg3+tJ>80cPgzp(0kxWae|!>bXExzx z^rp(Z_*=MfZ!-|`mgtpU8yzNvdF@-_&n{4R)_&}@G&&0gUtgH`w#ex%(AJx=@%^=5 zF0(1Q%?!nc9q%pQ<dR9ka3#CUgVFRMDc-YP#Zj-#4`g0;pWac{o7{Y5p zXt!;)4Gp(pFMcIFmoWBm*nD7*&4L%OAy5<399Cn9%$z+4B}~#Ezi|LEDRlK9N8cpg zcyF!ThmGhxHvbUYoA4Md2)D?C)+GlS{&0f9z^y^}-mW}({Pm;#;}p zX>?Gq3m9oQ{-J_F?a#&^PqKocc;oYGBih2@?S5WGTN>r?R~&sFI2-U|!yc@pJFl6UP>?M-dbT!h;!?t2Q2U(l&hX_)7o|#@+~(00a?<)4 zTEVtX=M{#9VQ45jY}s!|z(F)46=Zbk-0_#;KEn%)4Cg=g6z`6&2Jkg`6Cq||&klcj zaj?U|u{me#s7t{7^bW%lU^!tVDF@KWOeC{M3%uvp!sV_))}jHg+g~?7Tp_h*oUBUe51@>UD73GM%Ghlulo9>ZOiJjd2Evq-a_X zeorNIN(6k^J+!0KMx6CJXtrq2_kHWcxpi{3V0{YRS%{zJ^S9Db3wxx8h;KTh)IS6V z+STFhHM-1vvToPzx7AG-KQ3PJ^#N_HjWrZ-!z=bqQcCGb(->j?6>}ALGZ|zz_p<7R~ypbvv~agy%u4AhkKr+K8fK zSf>C?M%QUR?-?f(*)jtK*8kgeF>EK-mXmuOjvK)AW=E#MY|W+w5-GpS9|#wifs5h!M$hF-C*EYS)l4;Gck#iBb!a z{a?u$sC+`^_7rIF@K|{?5WjmuDGf%M4wedqe=JHk<+9lVIqO*R1ok%Ge_nmq=)+Ga zgbmbx=~p{34(=Fo!iAP)hKiAFEZ*AIq&6vYOf-Q}*AyD9pUh#F&3N`dvPMXESYgYrFrRt+fTks2}o($>%!x{Zo61Brs=!$LFRQup|qU7Z+`QiZfA z)9~YU1r!GT_snPp%>g!1h7>quL$4vm&!ey_V!Qo=fv!hiPM%~v~|5E?tC`PVS!8p)*lj53YsnSNdYvB5qH1_<}t``h$-X~6LUh+R-0b)6{c10YC$n&v~? z>BksCr|BK6blj)Npm>&}^)leiKolD^%<&COXv3}f^V#Ezq;c)3kJi^%#!1;oXZY@; zi>5YQvLM8=vjXuvq}NaDy41pNtkcjSVWATo^Zfef@C;Q}1@t_$Bn6Yx=52P@s*142 zs$jIXy96Qe?XPh9Q@VlhvCrsMOUC~`dGHypy|G8N9Y@|!E_%w-9eMFX10XBAG|w1z zBiE0I?IBfF7=6}&E!MQfCA{kf^V^aLg#x0{oVvqQ>flzT1Fwvvv~Vty4o#MS?Lbc# zMlaef{NsHVB)T=Z3)z${MArDkuiv~Jo*tZ>o}iIR|B*=KZM-I!_QBD7aB%I%x}I{! z$=|Wa>cBM_E;ggr2hR^);*BT)AmiwgjZ{-i)1Q@Fw;AQ@e|@lj{PNl1^Ea;#;0Dj( z^}7L4%)GcwBDj&WNadn{<&PZ~iRQt_c6WR>RB1b`4m#UU0x#%ASVun->KbscZl};R z+#NUn^$60R$)GXdbpf@Hk0EvwrJ45wsZz42{)(8B-7FS-wV&nB`|jvY1ggn(qtHgd z!;t8-dp<2L(`k2`^O{?oaQTUzVng>SpUINTa455Ug?V0QL=yG9=+*%!@X&x0G!p`f@tABismo@!M40R`oI9{8!OZLDF zY6X!wuk}0E6R5*k()+Ey5Z1$%zo_cg^;cJnN~hW5ygB`m-Ba~Tt{O@x2`yQ&N4fa+ zCs+4a#C1-`^%pdX+DV~vwecg$&c-GFBV}ZyTbKPE<1IU72(QdQG7`cQNzw}3oh(Au zSC@K3*^sAqlre2S=A3Hfa!%o~EWcM-i2*y)e3YZ6m$MqNshATCh9=#$DrD+Q9<9G7 zzs$Nkd~G(PeGrRkNzSMQdWXUT;xT)+=4dJaA;wKup}j5*)sL+glzmQ-Q{I#W1 zwNSw4e_!09g%iTO{QIaIFu~yxtb5;k&8cPhhrC*SPa4V1DWVbP7AeT~H8tYnZ4YN#)ngSyjq57$kN$p4Gi78E5q*DGAQy zT}y{D9NJk9y(Cn?jm~)JZGy$nFcauCu-h5$(sTl1;cZ%?<@9n{>x2I$AH$r}mJTuV zCM7ABp2N2UP*tyS!$tmSi3-0*tQ`xRLg2G#QABjVMRq7Fej=djLrvd0enIN<$uB6v{}*+j_}Afqg@vd%LS zNV(=1de(+eW=&66Q>v<`t|bM*R&Aw1i(mH3#s5(W7o zfsC=_b{KlF0x^9JBe@rZC;;*?@B`5!$MD_D2f#-Vrc{O&$bP@MwjTc}8 z6OO$?c_V~q)g5;3(#Eo8XA#b{tuR$j47| z8&X7X;3ZW}LYxi_nuBbEeyoOpKdhS6G`}p<@-8^5hLe0)&9)gG++^h)NY|tjz}Q`m zVom3Q<4D)9U|~N;|67o<$5??VN@Y=-f_D32tD0`g^Hy38)S?ayHq3sMlhIA79mRN10|6aJaw+Rx%7i7%qx zQ9fd;M?2TOp9qf;l9r{lnF$B_uSQ_~83j+G*H&(JIzYHEy`GomWU6H!cwBxnu9=y}QJuNr9vv9p$}&LFZ(8twC|f%YtWtp@I-C8U216fc)F zz%&4?6n`i@BgS}gHf`b7ac1dhIGz@|iR5XQ3q8S__ndc^(ylvUY*^KNhPeu`+)Uzu z{4rxwiYnXWyh(8@C2TL;-^f#{o}Gm69z@xP)ZQA`ES)c#0|=t*2zoi<)9I--91DdL zT=m$cv1Z6;le*l^E_$&h@i_igo#C7-J+8!I$@Ow=#QIQI{q_)0JA6fh3RD?6^TTID zXZ9&7L-jh}MYky|mIPJ_%$C){PcXnv(&LAG#PV{e(+<={&vM>!FDO+Vz{|uUkOwXb zY~I}kQ{RV2cA|nDFr2qGcyjSEBE1i=L+eN#k&O+dt9+#1buOBr2SXG0|B= zR=aeN9ey=?pt`ERuu6hs@6j^{a^GQZUSMUz_ z2g%?V-99~wv)N@fjuBXU-d+8frPWMI$6AB2pkV{_yqTpXeZ9DFbr4*R%|2kvcl)jc zis9Qu9d!Cu7)0;mr>y?xQ>BV`q7BlyZ>;Ov6Q5!znaWSH6UAR8kJfe!ir>wqbL=#} zuIt6l_I5S8&S0{MPS}VJy4z!w2V#3x(IL3~@OLA(_3^*sB}=(_DR^`39xm+E7gW+LncmhKfN}sluFl=V5k?Mfo$f zYOJDO-$8iI)+D+2vG7mvjx|6y(Enj&KT+%Uq2I|Z&_F5Svpo*YCYaoE>j2UDUG)3j zU~Dx6t3-{AbVYxMi;4*Oi)G+WZ2{T^Qp2Dgb1 zbgQ?wD}etsySoL>&jS4RR)KUgcYeVmvE-ZZ=AU6ALzuPu7fb$ z-V|}3@{6XgAY2<)#O=+{GXc)=EZTc^dhnWk4gSl)hTvkunb*djFy|Bv=v$Zuts%++ zml<@&0z+eH@Ir0%kIPF%p{KxLQprxaK`7#BiJ?_xrr>T_FGkVEsjd)36Qe`z=J8ig zqMqtBl3tP0@k!jr0=0O+UP=nF1l=AYxTjOBs?Fv_24X=@Hhhf1|1j1XrjEkV9IH}K zmQxaHW_g8k!SS-M2vx3q{M9>Wtb4ByRCn1p`P7?3Z3+bz#ySKvAQG$LRoQw7aZeuk zAH1v4^P!lZ2!zx?QU0)b+}ZlyLx7QLc!8sofzy>*AGsw&b+#!&#XU0C4JO=i3g^U~5v zPcs3>W-_C35F5c7C;UrjY3nNRP49{#gMrU5HC!-XO`(rF^rn(MO_M|iqg>{J=Dl34Eg&koFalfQJa_8KT1QC2O|QRYd9^QseZgx*z)qS*xZwKk@~Zlvyk zRTWAb8=`AKKQb`hBx-a@8gTE475#Q>Z@0@gRrUheNj!W{)9S6InW;hSia%TXUfWl- zZBFbEQ4bk!)K%ZnUAW7Duw3 z%3fTjTa~D#t7oS(rw8A*A^X-kEL<0sVY!^Eqg0(bA)Au+z{DgfmikYt>;f9-K;LR( z2UU!+iuZ}AYkt(e+x4`_4~K|T+$CSWhl8-zYgcQR?Oy}cbgf#C;kac}3R3B!!|J9) zk|g_^Q^^P^=IhBZ(+bBUQ#AbbvQnuADJ%s`#t57wI`rMiH=}G`EU&1LZZ^wn34zi1 z%p@X#QyJ-a1j?Op4VO(_c+f6cW~#TL>ur#I$PZpKc<%mfrek*#-0k7qP6OI{d6A5Z z7B2W_eiD0 z4#AL8eZw_s+qtwu{&~{(+2fsiM1XpqU}-rjBWI@nVBo+aTozR*Mqk@P1Xr|5im5Tk zM@4yOoiz)A{k6^%v}o-*wvQUu^>j$g|#ayJ6nscI&Fbc$pmP}!s!lc zXdZteiCu=`7lT5)ax9p!$WbSOcDCJ6?gx)fsVQ6vAa~y_O5m#0#WZ|3oA!(W;BTyW(TmvE-rbm}12=G0w5(c|UM>GS%}jhX7_k znZMx*ettJDuR_`att=qLfH_c=JEl#PAo6b&lPydg>dFgdb+NuX+qx~-?9RIS2&hIN zrH`)LLIPHPVi;gCq2ZC5y5IK-KechI&MEw98CKdw)*GrqGg2!TYSxD~6_H2(K5MtE z0uMBiPwx2rT}wV~Qb3#e@>6rJYm_8bms1Nr`>)9NvVv_b#+RG^AI3a15Hg6pgL;b!U621wB$cNwIuUW4VsR$Qx*e{kK-65B7waltYN5ru7VaMaV2V8wf>{QX@ z^gOzjD-XU6bx4_b!Yi~7v4gG0q7U zH%|kcIcpf!L7YPJzc|r+5|q8Mt&tpkrd0~-BF=(4lrW<9fjvXfbSz%8RC_88LQo@= zF?|um-+ta}zTA%+n_UL{tJ3`XYv`6GRR~v7rVeDc#?6_Touv9|miJiyXCzwM+DLTp zdUJC4@_F=X|9e=}7bidLN4NQON@wl3P}%F2)3j8PY}Wg_SU@x_inr)VLyk`QY#{+y zDOE6oQ*DOgF$(FEoD7OK6zCeCKzlUWr!`!V>70`;Z7N#go-ngBd438w2aG}pQw=~H zN8=@Rjveg3KG-`wJbp>K2w;U~iH`?v0jNmv1sfNF2X`zmYYPI;-<}}m(83H)*tx8+ zndGwK#DMV70wT?5hFMPL(aTIlr%CTaJJ_*B2{AepaqRwX@Cwe;g|O71y1mCicWjV5t7Ro%-B;H zLxMrhU0B6-HltpaTqPVMr?QBc@ZG+PVi;r>4yjz+)3i>%g98C^=etcZh_&%IzHEAg z$ja*DGq1F^Xo#*dLvd|Jes~B!mUnWd(s!c}z__Iy3k|QcS#gsY%{n_;QYDuZ%e+Kf z=H;gsM+^yAI9ES^N9uVzglHqzx2Qp|noxNpJngZM3YVJgrA`@F0*$Kfgwh>UIHO)X zF{Rvt9I0aov7z9G0)F%FAj4&K?=1fB92IW={d@ZFVSKR$IQcC+uophp3jfdf-We8j z0jV3!zh|UW%)G!jV>^0_eB}^Bl4ak#BFdg}4<3dpp%7Xdc@N-M!h!{nRlTaD61 z)?5?<*cvl!R%Q@wG{(AN{JE6k7W)-C*o;o#aD!{b&E4q8JH2@SNY;|-4NL^pMN%OhS6qsVQD)ldG{=mBvJw4I_&GXRV{}=C z{2MIze2f^mfXa$E;KW^iRUo$ zfy%J}=XrsS6X>l+oP<=v0bI?{WgJGt4>@D+tU&L>l1hS^l#dyuQpxp4w?hQ$kWsEC~B;L2~iiG(dQ_?OU56rC&=i-Ndag*-gA(i~=m$`no%@MretD0)F9E|49=*#R8DA!jS4n+Cfw zeDfuCm|}{R%dEc5fE{x%%R>jChqxsN(V^s_t+Fh-#wldDldJ)mnu$6aw@{3I&9 zdzZGMScZa>fICC6#q(m8!{vh+4QaE+`lvHgT&Z!C^gouqP=7ml^9=UH@bq|iwDUrQkfv+B6F*o-$#PkB&L4evX60$dAz%O9>3SnKK$doj(0wfo5?jhSR|Ak^}Y7gTx&6MtTLiq zJvPlf;jHh~jp4*Zll0Vx)hqfUiGnd*=oZyzSK$-51?&7{Z+m#@T^nsOUI(4w2P<5M7zn}-^+c-6Xg%>zlG zDxr!0Tu0RMUbLmH06rDq^+c|g@mqq|w<37SC_EY5QoB8DAyK14Ns8~clJEweyfy@0 zu6qia&HWOZIzU$zO;(M0Ce4+rVT3S8xs4JmJi|;Z)z0?ztQya~JS^FKYguhe>b~vE z%P;@%jdVEQ(raIShu{Cq-=E?4-!bUD)!-ceUdCC3xp%8+T3u^r)x3_8NdU%qzQv8k zZ8Za2*c|MtwA!)~Y~!Ok6sV=lu}*YZiA1XvaI2wzhdwSSr49JrfNKfFEVxkqcK$b< z&fm_@{}!$vV0|#UaR>hk|5~X|MD4|-lF;%^F(WW7>^g+kRJ!m#|96lbDu?L%BO9qA z&F{*WCR#0QP*w*Pud8KQVj2l0vk4dyNjFHg&!y%E+!p67N1y~^ffLHcw>M0QB1Mlz z>5JkA5#w0G$Rx`xbZtS~Fb1bOD^WnHvm`np`BQp=5m6oC6uA@xGfL5p5*@}fe9r>* zHXq~6gofdNDE$!>s+LleZ!1*@NDUTCFR8udsE-A&hznNG0#SQ>xFGL~U5@MZ=zS7z zyyq4lHsUTS=j^(aC=mzLV~wqwveL?^%cjZDSL-t0*f1$TP7prGNva4CMbMgc0+AOr zQCM~aTUbs9zl&sxfEbC)l*jy<$HtAeCk5!Oa=yQlSU}cb+j}E_kwRigcPAD_KN)8Awo&9mADLrjQPqVG@auGxy>RU=QkWitU zDQY-S1)gVKpn?Gg5^i=&)+Q_u!%1M2^lLI*sQYC($BN^OjsP%cDAf|aib+r@4mmv` z7mec4=gs>=!GXO^X;S$NfMH@3a<4?)&K4?8`*wev)Vg_#phke6V`gBcfZ!3z&k{mj z)}y}y>4~|KN*vs5M45xV<%adfb9O8CbJ636FxhH8*rW*9sCdienJ}A9C78>Vt`8Ly z7#ccG%dwHjD$4iZConxl>lYn~SB2V|Dmm%Y+e1lsV&=%ge5G+eV%oQVPeWv_$TqY< zxOY_YX55A01%fB{w6nA$S5DEXXgCnI#621oeMrGaQ|0Dd1|6Hp4bsYuY7|z=3bx}M z6YSbD3YPQ^R%5m_TJPf#5)yjIeF)XTr778}(U(QEzrFv(7q(Rg=ZQINCG+ey1{u#T zr+3nZ-7IeLHl>?0qs-H4j04x0DqW_0Zs=ByHZGehr)KG~AN?tMgj8ca5@jbna%bk@ ztUXt$M&nu2Vf>=K(DmBr4Wc;cQfPS;l1e3w_Hnwn4r-7Idw`6{&aX-?%3A5-Y_`d^ z)sa+avdkR$5S>aLFZ+NP$WO7@2=aLoRp#M;8&(6MN@Ww%813_;0vVy*cv(-j{s1^b zOY1lrO;c9jNm#T+DAur4%CRx}K;WFVXi~&jRR#W>*E*xFky(ooj1u}u#>EWy?Rk#> z5(8+B&5B~fld*t(?~FXYjhn;o;~iB@Jmza66NiqIy5Nx_zWeqW5-97!-3=mpP-1X> zP1J)%5S7RnnT3Hic=reTf#?ecwnoKztfcmVBg6MKwJ6x?RZ`HZW^VSe6=#|&ZYc8I z2^@27OYP~+-g1tr!(yY{mx!qAVpx@UmJEzr;)52nT8;oz4Cj&fQ z)!s16_iKD%%a?oV2KMc(N(HBE0c`LS_GkpM7aIILhb@cJTG>~Z=vFl|N~e}~Ys>&k zCa@+04#@l_AEP&$nvQL5luo}z8|rnsF!9TxfnY&U*}b-^MK;POw)d3qE&a#{+?nU4+GpvIO`ox4dbaXLn8vT@L~ zfas_Xv=vV?#jo|aprn4vfmR-BUEXX!{oao^b9|PXY=h;_bb+!wLL`%%L?L>^=z5I@ zawYYsbo3oT2(l|G)&*M*8m!ZLol<|SXt+m@imh8-3O95A&Z>I+u#W6=Xgu~PiKvL3 z3T*!(6UNF!gU>jNpZ)0Lh{d0dUGW z5^$t%Nes2UNQsMPVb1lug9dw^aUlJJXml01ux0J9*1myyIb7FZ3~B{7F;`i(bpnX?sUUtE{uo5Ex8UmC4X7M=jvQG12FJz z`hf=$)>U~knIo6)NTLr^E|lowLCGb0=m2w>O^e&UmnRG7fD!Lu56VHO>2KirT{cZj z{rJKgHtli>__Ojvkl(Ic-(FQ0rb5ny>4D<+H5=h@F9|CCqhb4Plh1uK-Ja&upAT-=nX^Kzk9D=tW7KDO*=8m{`*r zj_UHp_GAyP2ytL!V9_VErxrrzcuJ@Tn+zLqZE|7FZ~fu2)spk?p=}ZaT6k>&I$Lrn zz-KZQmG>tR*~3`OL+aE`{TfbqDMH5EEL#W~h*bGhXtE@#CYSDdoIqoet-}f-5V|;l ztCPlKobc};{WSR={9t@Ts~wMbu=0fD(z#r)(Oy*67|CNrf|Z}T)c)8`=W@z8qxPlm zfy}PoCz2~io#}T1(CIFgl0-d)5yP-C=uMeP8|;kKYS9mP3m!Bxzq*K1J2oe~o8=K) z<~P|x5TonNDO9x6%s7#(Kq_NXiyHfZB=b=G1`*68*1~@cA`lba4Whq|n5hD3F5Z6n z?D#mo_#3f(5*6ieee*0Wg?}8IME`P(RP4h%c6h_g6hlPiy~p(pG$*9e0}*lw3!h39 zbC@rdMX&R#Ycw(~${g;t`c9t}3DA}~K}^IO8=!3MjbIcY++c8owJ2sG^e?i@?X|fN z7ig3*I-!E12cHp^@z^=T*3OF2m3QpIUj59aZeThFxBcJdxlMAJP&ZHkPIi^Fzi)KzMsUc(tAPAH3WOioM>(qjJ@PTP=0zUjC^K%?;5G4fuSM03&{zQP>Fc zh#SufeEPH)(YdrC{z~g zdH9o2CeVw%&ZVLlv^tXn)4kHS=ue#GjDZrJ#Jfd^0ghrEpM$jzVWm2 z2>;ll3qR|i>7m=*ns)1!4^O=7Shkdvj@yoY0Y)5eKfjG}td^zsvlT$-oqi)lfn$d^ zcV55{?_AL#OT2reJBbo&EB1Dk57D68#!gP5D&N|W5*_IH=F8<37kM2-Dhq+{=ZV!? z8^y`Z;Ox^vDbIChH2P-u{ScflJU}!!hCp9ezN98lx@VKpMXATl6*cgb*TowNoL&@* z<&-aml;iaz$jL3+6<6(1nw!z{98=~SXID>dE-)N3CD2>)27JyU*w{&Oevani^K+n& z^K-ey$uAI|ZNvZY={C8bVZ;?31TUVB>E9op&_7i@-Wh%I#ba{q-ah7MoGD`K>B;en zgW=PIXSOa;#v;<|9k^`lB=V+rF!u@5t z@yk|T-Blnw!g6ow1WQ+=5eA-0jL^=5;q< z#e;3}Y9X5RS<3q{YBZHEG~+>8(_A#R_~9-vNp4LE<>r9F9$q9gRQwl)8lwH?=-^4e z??-%FlhNIT84O0d3?H3Ob^CsBEjV-Iv$3l1t+RPlKHjKNGL5}2N?0?_Lmrh(Z{}sY?heq zed8>hQiv2f<&xuEj0{b6UEBgQbqNxdZ^r!*Tgpj@314Dy*wbk|ICKofeT+S#_znqX zs4@(C*V&4IhCH&SS9KB51D-be+OZa$BInVapjnE4^SOT6DgF>UNJ#9@Fs&kKDV!C)+P5EZ8!ywNUTl3*U=N z4n_3cpnAeH4$~w2VJ<4NKAEgObW`u|oC^h=GZXOTlW)y$vCfc$*J~VEj&V`%CQrQt zkOz9#Vsk}Za2Y8alD(cIn_}MRN;aTeE1W!lb7nI-k5D(DsR1JaD7i~d3Vkb8n0*A* zQOdaU5f3x&1Qg$fH&Vpeta~`Aa z&7R3A;tRT0*@s5cES;m+O=%g+;&NGG>hKKcjc9>WU}3U!I?r=CYoZ=GcOU~vqwT9u zfw6eRLH}L~hhM|cu6Im5}i_4G2IqoX8dudH^lN~!#Y$9b7w%1>LA?gk5a zM>o}zM^B`BJu;X4v(w90(97{c0*y_E`q+C1{yE2CC)E^&iVG|$y{=0tNU0NaDHl3% z53&|Uyk_{yvujaq%D-;Pl-L|Pk)4x7PH!`WYB|E=pR$f64$!Y*Zjvu^#d!$lvi1`r zZslI%C^Ob%HD*NqSb_=6_l2jM-RiK$GDTB3Gv+`?6Sb4-f)nHBe0AL-!Ru?01Tf0i zn^%%Kl{XwI-i3#%%VNA7QPil;wc7u)Mb_cl2@s;OdD>U1i;2R34*wGrEB zkD#n$a4hgaK7PCHXhtaR7L(CCsp?RfU^M5*0rDhPZy z!pq8Ob6)^XlFoa-Ed+su%OCW<+zO-m-A*+8;pFJ>^kBGu`qKl)p?1;U{Eqj~DW?f33=mGU=PT*KaPR81V>q9xAyp49^>ati2 ztLrSQt9zHwc?IugWqo%-ueXlJ>%y*=^FEe}Iq^<}wSV3KT|)dB z6^vh<-X-vfb-p+3c7y86RlNsw-`{x900@DYM&_sh5o2$$z|`WZgQik#_2Sa4k@a)v67*yyZ9 zGoc(9B>606@*JL#@oby^E0(xyybWSa21C1ug-d3hMEe?AF7oPln4y5CbCZgG7&ALk zny-L&YV%wf;D}-`s%Wm{blQfvKG78}5c!C^NpGW`&gj@rq61@^VOUZ0U(OO+DGfI| z(?!O+yRjhTAMd{TD&F{$(Qe|br3jms0+S4%Yp2U}bnQ;g&5kZSPJ%3()QpZ3LQ~se z;LUaKnaJa?dOmG_2L=M|Fi0xl@K>493P| zx4gqJ#sy^~<9Izx?XNO}bK+uDjt0FKq=1a%c18V?lew7(@upg;EPfz$$D)102zZGO zvhuRiK0y_eN}7yXYn|p@hX@cDPDnEsZwsALS_vH9D2!%;mI-b~$E90~(*ZvY(zmOO z*D8&G1lOqC&M*xxJG|Gwo&7yQ6-PE7kbrKIu;-#>{Ncn^4&WgX*IMi5_ndRQ>|E!0 zRM8UN2=Y4Ur9!5oEFDwv05>DGVkabteLlB!P02%d$m?Hc2S;05q=}9G*Z+$C`NzUO z4&~RHa_p_VUzsR&4HL1pd#OFdx<9NzSKe!?_LEWbVYDVLB~tDe!H! z02ygvSrnyqoAx2VF!n0Hno|}wvcACCK2IxnO(j*3$`EpQRQ_E-Dq=bI$$4SRYXG17 z_#ye=O}YR~JZu{iRYoFmXdwHzWnxX?Y8vHIN&!?TV&t7x+7C>^2-1)1 ztg&1lq?zED?UYg?&$Y$=TwLOny*EXp?UhP4qi|1w$>QO0o!wzZTjES`h%Zj)s>7UO zENJmcQ6n6m1Tb*kv1KV9roOj7GJe}(e-WxuGo^D#66urKhN1$5gn6xqIDC1$L zbm-xozM4w;ZjHNDqrRh2tK+&WU$?8~tK}uz)#zFD57HpJO)w}&CEyM=2s+t@2D(<8 zX5O=4vq0W6x80fr@tk)8I&|&1EY_<=8d5(kkgaHvEYF>u{3aJP!MC3|3O%^VC;2-i z7^37zW@an>01}ij8!MHjZN?|h51ELm@ow~pdNNkOQu^wIt;X(+soWi^jH`lk4Qfv9 zB?oB_Zj21w^mE(0A}>*0bdw5e>}JdRv+3U5q1K*&efc9ALZx`-v4u6m9r)(Prs*^= z-y@g{2#T1aW=rw)uTS^6j^$bdqx|}>mvb0Wwtt*qP`b(}u z_#66E0p*}qbN+XyQf0h-_;UaC!Ha{Jr+Y^`FON+Gj!Stv9p{}pXpG^r24aoaw~%gf z4j1SV&lEzrzgwzSOAnkhh7DyEs@XcDDGsOct^x}0?@x6FL>nEL;S@2@>y5Johvn^> zu)Mw$MzOPlt&T19#y~?u-5H#~i<)K>-|`4We-UvVQ~LH$!nezu@EIJVw{>CVREczxn}Q)N#P3r8wf zb}QmKFfThw*Z~W|3)F3$UY8gzF{XZ=9vvBwXXb%w#_G- z*oq1H%kc}$=$BY2dscp|!oLdU9ctQ)PE<5p<(|m8+VC#0h9&t@c7#QCP3~Zh`kK9y zy_X#a3{5}cBiJ3te;;~1}yrXUo%~Ai1zF6Zr0O5RZKHit|y&joTL|kw* z{?DIgG*&Tflzy9|`F(ga{?DIgG{@8N2}+ggQ~CO^kxjMfL1;M|5y#XYD?I(< zqbFbQD3b;L5^o<|12#D9W3d=!qKI-x1=H<^lBl|9%dQp25={PY<&8x}I>QQN{fv1S z_sRE(RobuV%1Z<37Fw25ugCYY9M)>?Rd@4|A2nxssXk3FC(y}0+$Y6tdslPZ(4VBY zzy-*)CDC$Ojbb|U#=ao(k<)X1C`~m9)?qhGNrdtO1T&O&>1@>K)~dH|kEcbD)3aT( z4CS{jXgBx2HA^oSMOR9tl|x%yjME#HE_b*B@U!KdOxH&!=^n8l+QESQC?}087|Z#? z=?LJp(_{oUL@x$;Bp$4ka-6qgYw!DD6^}HX$}lQ&r}dgaJCoQ>xz^J3C}Uq;_8!B( zIUvD#v9;i&MwN8RpfrjcAogxpIqeMHoBcXHEq$M&mPH1zR~_)1t*c~fMmXnC6m8d85OcW(VcqyjxGpkyr#??(#`*;! z&rdLpqx1{Hfo=@%XZww<^Az=ILAOr)xp8LMasp{ zEtYsby*m&~9Jg*F@ACdMu)5>D`Q6DfCrh{^%>f^|C8>DHRma&PJf6xP4qcuPp`mB# zO;KWZ$0gIHb$^ndzZulaqQ*i&?28@wFA58%@+}~EKxZv6ra*g2pPJN&hBJa;kwK^&#wvKsRQNCkR@n@{ z;35UqOHD<<08LBTi8xQ8u9L`aN%U7RPhradMdZ}Kc>_3bvk8j9Ynsx(l5>C{aL=Y1 zB)U)WIK%^E7Rao=$|ey?sZOyvV;$;VW|Rn-DvTmTDn?otI=y`Kx2>aSYx+05PC~^! z1M$P;r9sQVU$^tBT4vSusG7Q`l{mWqYe?i;VOVV&-n0>jp{;X?wUsK2SIH31~{5Bg9Q zqB&d|&lo$%S6nqG9Q`~sRVY$BQ>r-!R0`PT9QlLkP39z#xSL&K?6FwwF|&xo{&Nja zsv2t*0A{Zysl#o-`JJAQO2xv}fC|PUdJB|Pk{i#^8BFx{o3G3m?c6r2SptU{Sfj*L z&q|{NT{d$oAvDYg=l0!KDn`F#woh0le`>!`Zi+HRn6;3C7syPE1BqS92lXEN-@AF$ z-ruL{H|&UW2=BfN9^*e|%z^8A9Lh&dc^l%|57@=RuF#B|z5#ApT>cFBa?4GG_oy_r zDpkpCde#>1z|Z-xY4CTd95`uu#e1QLeKhp878syq*ULtIMq)*B9w1cd-L2A4+|CCU z*k+H`S`xjm1BlEwc$RBw&&O<7#q@TxwZ#pJ9DiUl^>j;l07vP6J^o|z=uz_I%RfH; zL#XI7FFXmSV{%L68oXSJT~}F^;?gPkJ5mxzx17|IM~}ab-Ymw*>^=$h5#S~VfxH0i z=XWsJz4Nr6#QR)GR=k^4L1RAI+pvnwty9@R*qgf?8I9Zu1C)PeTI(@LukswSm61{RN-PKSbr9%WnTf7U`1 zZ%|06mvc(fLur)Y8i+Y4hUF~+W9>FT>yX9GK)pi392{m>=yx@brYPYw!yf(C=(*3; zt@x~`fU8bE1V7fMsgk@>g92bHUc?VMTQ?6CQ(%jVTMTea0~}v_(r5L-{0QhoFI-Gw zC%B@U1QQ-orTDYQ7iW(<@-?fEf8U4>A7PyBY@wmU>a$O|w{W@Qmc_O(J;mV7?sa)E zcAJGp6rR_rNwR)9l9o@fN=YD_x&48W4XxX$i{`D=%tRQ2z=XRyYgtwqL(AfYnOa8W zWSZ9Sm+W|sPOke-);Bo_5SyVKOF2#ih_ksRlcNQ=vrr-L4Atc zhCq+KGj+uV#=04u7D%)szpNoLQ<8Ius+lp@LcX5j&mSm%wMmxnc_CS;IAxp)5lnS} zT&8T{%8RuTc*wwr+vS*FJ9-FM=4^C5=K_ONCyl*VIpvctA{cP_Bv8D$F=C9a%&}y8 zR#E)Z66-n^!Kq@VCt8cxOwFs3ho4&ZWZ)>oXtYw&77Xj6SBDFc5)_3jk55w2;Z4oq z(J3YE19w@C>`#YdVqZnh}Tm$j})$N;`K#ZzLiR* z!kph>zRq-t{#tjs+8~gSieC!=(Gjs`VRX_;WHv+967`Iv3%y7nuQx)5)rn$$@|0!4 zLtryHri80TXld^Jr)}yj{Gjn&G0dXK_hqe>d2*?h8oz?(72M4)Dfy!!S7!0dT~gfS zAX*LeG?c#3?Ypf{=b{1_P0O)e^{U@+J!r_14fg|4l5?3ZE|oKM^Zg>v3$6Ng5@!!? zvkKP3T^U$+yp|(F!zznY4oKOdX*XLXT$;A`$Sd|L$}6V?(CP`qT>t_;3ZY?lP7SDJ z)x0c1a%N=jn|sd1es$F>aD*CmF$r0h1U~09c;*_F=#7pn2e#DR>wj>J2s)fcNkdIB zo13(a2!1)?{O|q1TRN~pbz}D}qEvg&DCUf%0#Y{Sd-SeYa<)~LQDM_J{ZCFCDBL<>XK#~>@^?)8Q6$6Qv^cF=+B6wXcmK9DS zA&b88&cEb>c;%#84M*v&iGCu4`uJkC9vY*#s1Tb}i9sB2L^xkPVf9mTQz=sxMm#rf zLc@u*>Y6+lcu(mtD!i7AYE23z2E?8)@e)vDDCUDf;aWzvj#YCpzHHkdmV@+nX_a9Y zFJZa-bdBT7Re4qtXk(>{q$lFD+15AfonCMwH-e|>_=)F;G~K^0@==z2_vY~EX~>CC z#W!L&yL?Ti#cfvF#_k5U)p(b%*$O}p(!8;YN~HOhPvQ_@nTF+#M1TMEhi^QGmjG1E zmBrS)8hPfKN)|V~8o%wi?fn|0oqYq?m3^n>>^}3^d;MZ9n9%k8psqRAZ@$V#_lA71 z-{p}X)PBZq=0>yV3BoKUN{2fUs+_z|!fkZ>Tt zauyIO8^L8NaxkV60`b@l&B-S=yfqvkPBMuJSk4-Q{)|T~D6ANTN|u&9IVlF1vM8o= zjuaD<)L9wf#yZHKvags_Y?S3atuYJrvX*dVGTkxcC5d_qIM{2G@=uqi?nvuLMsysC z$xLlVNcqh$$&(}!=gbFHFL8C-&Yb&p$)I3lg z0B`3MjVpboDm8PDlOFW#9L)ns@NlbAFO`{Kfl@D6vtngO)Z>g2D#1fO9~C7RRFLlI zb;;#|ej1eTl2YCfPL^{j(!4CA_)J}p7nlytzUar zO_@fDGAk9`70A%0Jko9DaE4kX1CjABaAlnKXo270+|?%Y2K0()gNfr}4@JsqKR{JT zVM=^+>R~RlhUvFex-o$`W@y|c<~0CHN8D$Y5}y3%s2MUeK*b{fnwfM-l7U(eX2&jV z=VzvNa_R;G?msf|;lq+1-Xq`S*`o_9Ga}~<3*<*cg?legvLGQ!dPH|eSjYLlWS&x! zCSa`;0{-itFwJRrWdE$Zk$S^4_6AcKF$ZRDbKnO^t<^EvMt7_9UurCPy659}QvZ^u z2enpnkhyUy_w*L^*hc;P8N7Y5a>-Sl|w9y2_qZlBd!4nqAY1Wk#;qa z;|Y0*B{1|!qz>>mbSZEpmAo7+XDnN);zY~2slbEf>nJS+H&?(bbb%hQD~(sA7)wg1 zllwN)$^U>ITjxQgmkCMh=15Q7gnpdHf3DKR1he)_@)p>-C}K%1QB<HV0swoM>h&2i1C}Otu#lK z?1k;ca|1NMnbU|PvQ7MKEXa_5ay>V@JYt=~2&k*Mhu^KO+wuBdLxPyZuGp~dv}jY? z%0FEntp6c)Ro}{+nJNS{e3d$9+U*#EaT3VH0Ml4zrWnQ;E|DwTTa;}~`OwMbgk(S! zVC<_!=lZ_2-Je31y+Us#vuW8wOZFUXM23NOJ<3o>{FCC?N_L)FWF-lV;)Fuu2z01& zF>(&kiXCuNUzj2klcq+tZx{CPdO1H8lOyotcE+&kZN9LMmZ0%6vm<&jGaGC-SR4#* zCT7}N6g8BlK}u>%xaBj>7;C5Pp?kzBmhh5tP+G0311`%*` zN-?a}g&?gAbrZN&$~tEmdte7NbdImfzt$e2({A0#J37e@;7{*h8wQ~NIw-J7(Ho>b z2VAS1x_WBon&BJ9)&B`ip#gTy(ddoPHk5KX|24)TK{_kYA_*fu(RkKUAWh;obClGT zMt&fSR=;*2zfGZQkU2{$M=6GXQ@*2SI=(1Sb$xF zu*V;RFmYKBI@wiUJ_2DGriGm6ldP(DHJRDJDoT5JADU=)@tZ|;uG$7eADugZeQVS+ zukp~T{M_8}0!aQYN|jC9%XFy@1EhW{3P7ABSxt03F1dH~mcuiQm{vH)+RiXK=rM7j>ConS}KRgPKYi z%1RAa(|G)#!2xfHDz&wT*P(w?qwm&1S=XLCO)2nN$6&*%dkIP8p-N9;o~tw}(m-cb zv9|z%RIgIyC)Fx^7@6(Sb>s~iOE&PaG_bbh+QPT&Eu+4%on&0@#mhYpQ)YiU`I#1( zo_K8Cz&ejS*B#}8xkl+gnXtmSlbsQwtg4S}SPT7n zfhyGZ9~uXfkT={L)e!m&%X;M4n^O8=ow!L2K^K|?679t!_%9`k^BuHgPDaxKf1HJ@ zXoyM@8iXB^gTa9EVlYlG3#Nntk-1&25W3hSXK*QDg;5k zDXC?|xc@vU-@Z(g%sbZEQD#_|Uh^<^GJH0qsFvbLEy^Q|{!npiD4(dmWyi9UI}1MY z#2R71`PyfG$WEewhl~lj;s@IkV54JE#102mD z&^x9!T2ci}GF-NMkYJZ$u_seu*?`W5Kk|8zlmN;K(h@lG5bp{jFgNs5gni&xNa(x= zFM=OD?8!VVRI#=%Z0MA`evlVz9^?&lxKe|@TVfvFX)AnSy7(hurYyEfpts znfUprYtxDh58Rr}&EEUcq_alty^lG49~L^Y?&+&Gb(pF|x#ZR+r2S!q*%Oj`feM5& z4hDkaS6aJZLbmDVxPU)#J_ z9Es}F2!7_0k92&&2v~24Cs>b%If(Qsu6M*4<=2C7BMbN&wSe6jCCqF_*Y>TUgcF`a zyWFSWZ-m)aJsX0fd0PXuCdH;w=7qpDl3=$&v=zc3NX;bQ{-;T6_;P$q7JCzMr2->1 ze%s?h-~A1^lh0Q#LT6%(rah?T1Z%x?!Us(CZ+Wz|4B!_yRH}c%aZ>%~Ao)M~AVHZP z&x?dM4h0GGzOg-q6z|Y)Y<2tdFMlR6f?A|GvP%ThZ_XCD5tX?1IH+Z&MtFh*jx~I? z_R033jgLKq*Tq&1KVUEXk|zhF<<~eo{>685LtEWY)}DRYoy)I5ly`$%_ect@i^8faYSWq|Ds(g_cpw!zzfCZ+SF8OSBgBH{f6KL|-~x?qtHzj= z4YKGr2WELUwPr$Tg0YRnT;miolXIFZN?ali?owTw^Du!NkBK4(mjH(jXFTuG{~aXQ zYG6S{V+kz++DIzBHkUfd*VwAVuHREXab_Lm7KTfL-O!xjU*TElk=PF5dxAqkJ4>&} zdG|0%ti&uW{fmIkOmMPFXC}>*lB|hT^&~v>kR}bNT4wbyje{EbzplnXp=Mo`C%fhe z53-J~xAL-CslEF}3k5wyw)s?(u=)M)f>Xj2c;gjc%DZL!+ujfR?73cHx$*w_3zhjH z*$9_HpW~DEQGQ*WLS>AfCPpKRN9ZV1wSH>|>YVT#qF^24ZZzp!<1Zxyx zt5m7T0O1p48!7uCeDxe_$ZOroK{xQame6?>=tR0#3OHO=RF9A1jB(5u(7zXH>nST& zQw%S!YfO7PzrnoWh#P>Ysg6iXfQK5~gdCYFFS5gq-8CLO^rIK0sR4wdHLd6@CuB1a)Na)$S>*NT8m30B zQvyE1un(#eVIsO&#lI}AH4`6FgY8}{dk%Kr`}njdrj^QgwG+i(C6D5D9n51`#(9Nr zVLgbQ?d@uGox%Q0bnY3d3^B&aW(9}=+p~((89aIP=*#$n#h}oPVC|+P$KB(ylQ&7! zyU%;8f?E}i8$qrbf}}lAIW|QoKpvGC?;VoCX;ooS`~ya10w|rBeKv)mC$3ph?To9F zTNIpclzagbl}r_*8d)lFQkpv53f=mCKTg`jS3o;b$^g$`no7cc!7ThFycyQctw<#a z)XE~B7_+Xw8Rop6NC|k?Lse(Rv8~t5vhpcljEZ<+^fq}HPT7~nL2xC>6*Nw59CBVT z3g_1Nv)!mEp(B8G(rxez%P1!vL(L)>M61w_rP)Aa7Cbf?23Z!)-4&v)JjiRwYi&hC zf{Sn_FCjM@$2L}9qF+-weH$$oDm#s2e)AAY`F&IkjBK{3@3^+bEE`iw7LxO9?>I@0 z0!>^D^577SErWGuX0XH!U9L}&m^~@{z~ws4+EJzA=q#pKht|nU0ex&f(H&xsEi0gg zqyRQrb@`jU!4j0)F$t{yvrMaNK{e&vz*Ngz1p?$TWKz;7K8Ld%z5V0QpM3rGC=MG* z8)Jga){D>2V~d_T7DOszZpSFq@J~(Tn04}xoiIW-K|VLZ*)mNAK8P;MEPY$iE@XGQ z-k;GA1hrmbHn3bmtYQ5dRUuP+n&5xF>h=4|&VgB;KUf>5D~dE6 z6QvJAGagKZxK>b&bH5^+nc3Cutqd2+%U^_YylpQ6OcXD+oY4B6&?dgmAx#5Zh7w=DvX1)=o?6ou=Sju5G+)~T@N@rm0v>1=^Xa<~R57Hpa$ z3ua&bR#)XUtl>AD{g zBd8^ArFZ1?T%%$-MU}ARu7mrO%D7-WjIk1sv4g9FsUS@;L%?>ndZlW6wyPU6|8{aX zo$$}`f>WsM!%|=|f|7F-b6z6M_EDGl4QBp;x5}lHl6X@oDBzOjY${t@y6zbD%`2C1 zy}m6ef)Pg<#cZOeL1#;kj9^)Loh~ZQGDsi|Fsny1CAPj(VF&4*Q-*DA0X9_%tr*ZY z&iay?(91H2dT-cC(G_x=I>}zj=^m|{83@S%2}1`ey)$VaQ8=RPD&-Q^rF}G!MUQ8i zpe~{_I4JSQIC8LS_~Oi_H7^m`ruD|N!=GLp>|m-GEVqV~fMHQ4RF(4WXS_ljIwK}8>ky+iQgtciVfdqm$ zJvXx;gq>q}X3>^z<5XHmBGd&ih- zuRYhAe<0*-&JVJ9egE5adRxkLj#r)-cETzqAa%nVw!+pGCG$CfVP#%p&6V zspZxs7GSNU;A>0H&ZCy>pKyDjy(2XKYz#I=#`VZ`d8;2^t>Y9W@ai#GFK%795^U1c zl(?B*iKMY#AKbq;bgrxH@Zox{uE)+9WP9ERizwn;8K0A=t(4?VMBUrtp2k0m%AX|< z(TP-WN*<#QiNa+`YW+(i*%Mht^VN5wf>iRW>F2yM^?u_{$8Do5M~4S=c=TG_6AT5#BW^L-&b?w}M~a}VN9=|ab9CTC{TjFKacM4Q7eei}A01&tdjc8K)UT>LB7L;xY}OfJ^cXeDFLMBJllAE z2+Mb^yZ0LE60lXX32_s!&2;m`Twx*dQ8WGoZ>+WMKNiGO_=HzVFaB8p`F!`Lc4kHp zZi#;}%+6xn^YAfgnDhhc(y~GEm!qSi@i+s_PEa|HzNxdJf=V;c_F&yW4zDr9HqgV| zXH|YsgT(mIiis(!iMf!jM3eW{Ly$?na)<@bWp!Oh9ry5z$f4;nbkkYxI%`)Z+`A47 z9iBr0-Qz+IxYj#oT`01j+(ut%6ASE3hU8G9b{+5!C4f|DHN5!agCm@J80BmLrYRNz zYB=MTL2mSlKTLJs@84G)@EClgraWkNi24d}IJ7Zi;tr-2`E!1=W0Vc&InY|Z;XDV) zY?jfnQS-+t4`nBZSw*vKe04iZLEl3T&*iYmv!Jz?rHoh8C**WA%!-&;&WKgGC+PAf zBF|--F%!+9m*PBrQkvwq2c*^IaN>RY0o;zsT^K16lw$}A`3B_Xc%|woD?Cn1NVv_FK3v(;`BkgI1gB;W&iHEuBKkba`8B5wAH|iuv zrczqJNgh9wiut&(H4v|pAL@0QTY6KS+gex|1W1h|&F=$VFx@sQ@fWY4Ox>MKAHh?} zOK(rygh1cY`{NURHMLc&;zDcp=Dg!%G(KBB?XRiC^(5fbDJTcN`y##)4&Foy`UVra>;;hu5dSLv z2rdi$jNADl=3s`e%&$j&X2gib9zY3 zsz4p#6trG7eUL5Qzxfmv1|E8ASS$dEmObw7lGDs`^zUxn7T+YkUt%90hr@C{xm$Xj z%B${e{vC<}jeWr)q;~$_p^A#2oY{B!y3tK|G>3hJ)M;JO$Xyhe3B7Yd-A1dV4bZ>! zDalos&G|Eic)F26GE>|>7AW3!;l?IVDr7naTSnZdMui2=CI?rkWq*?afM)U%rD{mP zxZr^x+)xif{MsYbF4})h#A`n)+vSo6-1+yV@UOz@H@6Ac3gCl#PG65qdNw|WwZ{Rg zB?&B9J8A{CuWha!2$k#^$aw&>P35N(ulY13Jjv>`s?JbyN5x@x@Cs|QKMVD7U8-q- zwlDwGh25Iym3~ok83M8<$bctv&epND69bhA~kq z+iz6;2}f2+8c=)B^zmJzBO|vdfY^OcLrZ-iBHso%QGlknm{^Vrf_%8*$aq_*9)n_> zRUH1*>$ZJy;kl}tL^uH)y`S@$nUkY&+%33tHnyJJ2b>HQM0dut3{+9Fal;;UtD%Wa zU|#_=3yJ}@@CW$d;76f;fNe!c+Gds%$Z-6Fy18HDaIX#dlkN9-Fcg2RyU<>c8vnt)7;DLYL$JzvAU5+UKS~Z6wEAWXF-7HOZe>!$k z=RVt=0+jo*E0($j+f3vuX;#I<3^Qz(!$p^nPH$})@{i=pOQ&H@Dj1bnwTwSETt=R= z>u5O81aTK+6zkWQeum#JP=6bo6q{k)``o2YU-G%e*5>CNV~N8ML@;%4%H0wxlo}jw zWcgr-MRSBCuUGW>o!rg|Mci}f@1z}R>|t9+kd`2eN0Ey}NwgPTBYMrsEfQ5(!To#p zawEiA9;$aa_Lz=k_OsUw8hRGz9c-P|cFL?TCx-T<8wf(}r=eS^n~_Be*yqt02u&lUW4c-CK+30!4P(_qg7qq>(5v=TO{VfdR4jJ> zwBk~s@+Vc$I=pR3K3t%LapKd+DHZMqkRKa7geegvsxFW$K!nLCKaj;2t3~Z!esCpk z)D1d^=3$hPn7aNN7qwQp$$-g;Z{MGLVq2L+-LkY*nlDgND%yU!Cx7k=8HRl)g91+J zK2Fq{52WY3H-K8LQC#oiTd~B=C z%>rqRmGPST63FBCPY1X7z&xzWs2lTCmSFMEld{PwJI6Ir#6kx8R5U%QvCVS#!J#0m zvoQN9rPok#2f)!huTg(!smfxe>f9ss3OwLKyKKvVaZ4g9u_x6nZsOE-UMQ;Q`QXw! zWKLm3Glk)@s$al2oZfPo(Q&KZ+a#kpY+?m7Zku&}e>49zPsux69FR_?%HdUDdJD*B zE!sxGai(W)RoK<8jU|8lS@(|-+C^-Pa-23mwmr>BjdJ63Oyy_xa&C1_NqNV`e^U{;_X4=q>kEG z37Cjb%+DA+4n0PTHM2&J(J#otAjzeMY#M(UO!ws*=oEU1Ri&Y<7p}<4jO-_>D=KH{ z45g*fTE(7XLUbvL0j*7T-GZvIMuqdbDnUAfY7cpoALWt)NrIdGE~IvC;U7Fw>E#5{ zxO^;GyAbG~z2h7miHaA6vTFTu>YYN@ON+9r+GM!@VGehgr8?H#m-3DB>+cTflVy`2 z9p{|vvBHjgmuP!1M|;ljr)dD$BVDq+Myh)9AtzV@=W9DmX}k9i$v6wUVTMgM^Tgy! z8ey5Yx7={blxkt4h=Ho7Oh_Oa6;`tKGU`zo{^t}=g6ib`3`8j!Sl0V6(;H`c(%KaYQKTgCTV>4w!Tq&CugL?P9gR@DCv zADMBqso#lZj?1@=G08p5PuG5Y%$`9=V;aw(?2@HT7UrBWfh}Q+50d9ibkS}#Ph#P1 z#;uNrqpFsQjYmitbyKgK3boI0+2Z((Rx7!8`pY}elRrt+Q7raMXWN&M<{Ay%%*^@o zP1V4&e_PtAEuLD|DQ*Wts6gc+e-H012z{p0&ez^Ta5Au5XE>$z55}CHb4l9;Y1!_; z-n@*I;TtyQL(#03k-6+$2qyf4Y81{IAumyj13aADgHMAqCh1`$t!$i_I~Qi6yItFq(sc7ArpWp30({LL+Uw^KPU4S2;r7o60X)wLwca^%nckXy= z<8p6;4_p)Aun66;*!AF7NSU^^vE8?>Pq^NcG*i@9;2vWS>|nk_d&LezTVYcWJlQx@ zNGnGaJ~cjSb2EPEBkw8hMdQ2PDwtGkt%hZ}u=*?_B4~n#ErqJvvbqJTUeTy1?hTW* z@Io$T4@x@ef>+vVwB@3ZKe%NgYh^JXqN|AM7l>1Hv&;#O%c0;hV2;)v44!!hJXeQQ zZC7V|d=t!qzp3b*(pu~@Z`K?}W)wrF6?Q?p3ITJ)asT6NMg2(QDRNvzZ}wE&fhs9G zV&Hs8h%f&6r04N&tyOJdi+4=0gwm3hkMxLg!PX!jnSAUxd9ak z9^SY(`D@b!@vAX~?&(bgSM37|_k=}zy}SKuVmcoL#OKNV3T9@KH&G~W_wpV#o4A~~ zO}5Y=o{ugq9)v^(an#}<52_ZA_tBb+8R`cAKgUuIEYMi$Z!E|Emsqm?_gG&4 zwI$-`Rqq}l!Nr^=X$c&CY)#rkO`i!^}{@pkv~ zRW3cri?81&O*h(KTb?!w9EwoQP3iv=R8haIFIkQ`&vsDr7aXijSxBDjt+HN)V?Cd$m$C?4>k!|A+ZgI5H z4zU}Qf;nne`CIt@RvGv4p4a{oa5|VmS2Q(WH?z6Ah6OFxpoP!d*uw>+ z)*2I|QpG1kE@75UGP%mKdp;k6FzW+w6`?jEO0flT5svDc!+DfkhB-tv|?mw4%aHl1Mi!M3~p- z4zNTv$luQ3`Io|C43yRDvu!jB5z4z5uU`ptrpx*uWDL+iizvY;d774vMqIz+*jj&KqL=j z92Y`s0(Fq|pzP-85tPOkJV`U6j0QmD#)N~i3VZf))h5^BHoB-YH^D+;$FM>)HxIq_v0IOrH6a+em{LuHV!#ZlZ|7+Dn|%~n97 z%Lq!sT`td;;&2#|AM?Yme+N3nBTsn5i_o^n`_Q)k{bGEUAV-tN!9~;u206Zx64Appu2J*zRbP#Tt zH63@Uvlhw&zD`Uvh0yx!q>l9h=qa!q zg-Z$o`UN*cWnGDjkr%UbRc31ENSP1F`_ZYk#n!sVHOR6x z1N|7a8KWl<@={7t5uO_IW{Fk>e`#X>KQwXLYM731o!R-k<#d(CFus4h$zO`V18$-3nHSK^bDWQHpfkLH zCtbI%VK0$d={euErs2q&)tj+53a4@D{{by3k=TzPTq)^Hj;@Xc)}cx!k=WJR5=u(c zRh`@#EKxz*q%DC0cA3NUXS_#Q8$B4S@vIiUB)s3!|3%h+P;Yl#W2&egbf1tVN>wtWKJRY0v>$O^a&Rs_`Y1`zmQzi!*g>d% zSvF?p=;4ZfpAv#}XK1trTlt_Y7PsSwcntrbtbtIl#T4@$`l*qI@lP3WvW^8(GHtfh z+uR6jY0O_n*rU$Mff~+K+zIp;Qjo3Q`6LL4fY_N5@Y|eurRal9Hl7CUSvIp)ddDqv zI<`~`tK5!1A*eX#XeYgBZrAOJhs(40RdfU;++r`;f_wgk;CXySE7?uAWAv&d?#y~8rctj~1|M-W3^6sDfd>}N}eOiLw1 zE;j8m7vNVNv)l!Jx?0C_7S;>|m8l`lw6+3X`y*_}&H1+hSehJ+cKaV_Uz1=4s3!t8 zr^OJNh|g`uAZe#iZ~=#V8zFy&tg~O*9>{pa`KY87`}1g!6uV8r&ckx`&rELg7d#tEVx;o;2LW%ZNL3^_@a!@{x$0aU7dMzCoU4YgIAI0q;<+ZXIE^3 z&FBXU>je9ZA?*4Z#UBiveHbW?0b<2&NHO5w>>U8~cYbl1)NfI13bc?f@P7uHQK6f< z>@UzN{~Bnf|MtcHPj&wvK_xyFSvTW+@U7p9i?bx4i&cCM3Q}BVD|dkd>We)+nsw6m{AbxrDjE0m+`kw8FMt$s`R5&FqD* z2y}eJ4*HRCO|n3>n>1!P_Tg1}B^aLQE&jaGV{F+q6r(Q=#yuW%-lsLyc!sTfk&${}zncDUT+j2qy zcP%UgD884z4~y|sV^E?^as#MY^wJBnH8r_N`Unq6nm7RsWt0Oo<#-A?)ox=$v=uou zqX8os|T{uB>t|93vU^n5=($UZGoOsk`-q0wc z^(bLeM!c-T1M+EGjDMQzYTE%3VlKer6nlFe7m(5w5DAwKNasKQV_(8j=CC zSV>ThyLH&r=H`()gM?O+)BE5fJ(IC=T6>r_#xLJtQi?meIul;qH!Y&x!m?TlZa=3` ztFEE3jF2j7=5Yhr-iiaPcV^RswB0*>-}b8Yk(tC>w*I?5Ba7Zz@{p? zW5V`NBRix{zw+RJaDG+k^6#Ac{Dbp{e{jC`icKJpBk3HJu>&*~eL5v&!}>NICc$ER zs(ZUs+xf_c{o%9&A^WS+_=_K^e`3CwwgTr6-%DOVrL(d_r*-2s`VwM<=GyN%-Zd!W zb9&7>S66%Pzi`g>|1aDdqTJR0Sl=(O{YT;ce{jBG zi22_+_kLXc-#NcP)3Ejbfd5;x1>uBJv>r&%*VukI*0hpJ+)b!rCkEfxR5X)GOPXXH zN@fz81Cmc|pDGR-38&&so|`u#F{>fM>%cia$hLEa9~0ng1$yU0I9asRv@b}yT=d}m zWV9F-0bH9}1K@NLdK) zkZy~QD6iud4pSmh%v|}2!N)}GVVoLOB?(u!$zere9NnZ<0^y6?<4zhpM<=h4AL%if z*@VbBp`|UC!hhmTQ?-r-=Weu+LmdsZL1y||beH!256A&y6l4Wv(Jl{TgjTgF58+#g z)|Di}ZrWI|5Ij$PZ{}m^CXi&Cltz#Xf^a*^b^62}@@`*GuiUTG_ zSgX>?`U8~M3u|RXs8fZLtth9C4fgs`T3k0kx0ff{nuz>Zyu;$Py@Mo^%LiXWE5Bkl z3x`LTY^F)tvf^d+7R~a%RkmXGCDAc3-Xn+?S8s!J^Rtuq1B@jVrP!(=R?~fLvxicn zh$M}F=(9_A{^e?#Jn`%&AAPn}n3=?;0U-ZC@*j+3E*a4pUE@)mFj*Jg1A4C z*tl_Z3zGLHuB9O}jp*|&Mf5X%j}K^?mK3YH3ZyMm_~yJzM>KFUjPvTqR*Ro`;27*3 zA*mD|NO%-Gx!_z^q=zSl{f=G_SFnvlCxU^}} zebuyB&L}E)l61x2CPm171)L8jfw_ygyUCpLrFZNW{y@UY+5d#4M*_b%l*&4c7SQ|6^2i6SkMs~&~|Hb&F&!y`=TjS2RFLZ2<7gdSL zqZn5Q7U0%;tF^NzV)LotPsY{x(|iHx#1RXbrFf-H-01N|(}mQFrloO^`VqwgT{~Te@?&b@4Zq-mK_aBFzJapq^C;GHZVJ|(DTM6N!`+lGwf4N z4p;HQTd{X1@09K$+e2X_QBJ*0a*UZtaABaircH8qCqdlba7pk_aur%AM4!BQx^T8< z1WOgG#qlQg{u<`c^bV~J_be0tGY-s)`U|;|Fba2mHg7-+Ga$olh5y~12lMiiW9{9- z{l)b?0+K*Cp`4=$E#@IQPJC-pgig9$A$$vTUNoTHXVNZ(KL@jY_<1Z6O0ubb~t&m5K)at>5d{ zQE4fVcMbi`gM~|g2h^$Y2S&tm*8{etQ236JY$89F>K6q&xBThY#S-piH{GK_@X%j}5vLOxdpS*s%_2hZ!`02xPGxAsTiA|4m z$cEe8zc0}5PkGD5dCFbo@o?h8KFNuBUCYhJZpX-O6Tr$^wL9$&LFD6I;P>!u`K%_7 zKf``)a`US5FzwO6|MShDCITmy)0D|iCsL*4zin(5HxUc@p(zyBlN7z-F;o* zkw?k*!w(rW15v)DgZjn_4opwX*!TRChzlvY)?#lhHLx3pi4{Pe*J-s=J54CfHbqSL2#XdR) zi3G^Wt3HCh!H5+5F+asSobzHR@gvBRQpTvhLaT;25gpK@26n$ZaUz%R`w7I}=z4EL z?zsbauc4v47XZD5&j4QvfL~GXcWC#wYL8z#NU%e{d&%AD*xNEN5l%JRV!rK5{$^hR*zX&@r zogR;ZUY`KF-b>A?()YDf3nuq1EiATm)s{fHowrp3qBuW+e!K7H%;sYR_PaQ;as zrVSR2!w0+cQpR$_jBA3Rz$Q$Se%g!d_azXz)-h(SE~@WBsXigiyKZ*E1^A>7nMO@% zNQ=zkUrH8;CItlq&L7>z;8pA=TKMPTVmg;zqYjt?x8?}}lu_M3oHH`qvZ}kzDSzMH z4Qcgn;yQDSV%KUu`h>zI>&U)Y{P6q}xczNA>ZKbrF$K_ZC)&FpOB$z^8LV*g0sq`h zFHjYWvGHSjYc(I=gzaiotUI#IR`G za=~-`)MSDysB*zejy~)Zk18Wlk<$SFIl-zt08aM5fRK(M!sl6|%+| z>JvhfaJt@JPd>N>s&me=U-Ry3r<*pMc;nR*@+FT-P9TyYe^aQ9M~3NvWGh`iEHv6Pu=6ysnR+`en>|>BiWq_u|b`)~lPsw-jRCfY-IGQ0KM9%R1G1 zXF%@nMJ>y+^MJQM-=yjgRr^1<*1C_8;5V(0m5)S>(B%!>g}*DOEXY`Sp7VSz_c=Jj z0{iaTfYSY}1PqJzU^VZqhB!R*`0vltySK~*TY1rRVCEh5?F$oy2+OetMp8Ya_mDhe zhG7lBR12#_8B+QCtpCn=5(!p|Gjni0Z{ zrMmV6(QXiHljI8m0MYY=^}-8^*E~iCP&&|jzMNB!3-S+&RcqF;EM)r762S=K-Q|AWEE9aaOkF<}S!0`&_aA!a z^C$=1Y`!|FTL;LodDEC1qUjiON2&cQ2dQ0C|Nn*$$6DbDG2Bbh4|IqIBvq|Xdmp!Ol~Y0uealZ|OB zx9DY36lYW7$dX#BF_fG$PN1R{=9i82L}7Oy&6j#->}eHkwPC*3+DUpYgGeVn=*`@88XMJ(9U$$b951&*N9zbi*LB;Mb zF;L-<>$q1za3<5mS`v-gn8MemQ4Z@*2x>J?{+2~q(<3It#xGvR`{=mtpYeqHEl24i zHofJwuX3dW0{`$E#pN39QD&}za7`tivjY-NX4CW!@}{{`yc1g(d708rhd(Hi1z2@u zvM-fTL}iN%#m@lV((O8j(1l*6MX@fQfnQ>qOLsQ@G)DyrU9-cAVqBE8E+V|oJ;S2E zBQL=l8En&SxHGM&`jN1e989l zzh#EVMVt*S$WH&2(d`e)VVWnSHVuK8Vj}rYpP9mUg{xr+^1=0@piYa?;&+;eYL@y zwuXP+cU*4`B(5W3{5*PrH(ZW)FR+zXn`FFnesAPUVOE_r$@~EC2B|WqPR_KyB?G|& zJ%*f0V#$W^n2QSOCpjWrEkIf7dcUeg1vC$)Klm0w-ToT$5kr zjci;W5;zP_w>dMC__uH1Cg_32a2$!Y9Pjk>`USbNIWWx=4Ercr2&cHV74w&pQ`Jud z|G~LtKI$L(3|K=BMNGliY`R*St{{UyW*)^L(C!&0t#%cNlC9gdtd!e1Yc0ld*{POM z!Bsoy;F)j_TYKWh6J;)tuI!_9(ly{Mn{Kid8Yj|dn&*u|oCago@8k2#0hUeIgQBh? zy11ZR(`HwvJKA~C--_*lTRog6{CwK>evlKGYer>6m-K<-q+mH7XFd@iTX-T=XoMPx zkLI92c)tVbcns~&159BIp`~Wfc1}`n0z=v~Mf`O7TYWd4xBDN2zvsY)9KEB^Y%L%I zfsB75fJKjPVON6{wtzUJuFV(5eNj1&IWU1jM5PxD2qI3L)bG}EnfB|6ZdEzq4L5}C z{#qh%uk-igH?D&+c1_V<#E$?fgaX;vc`;Ba#_W-eC)NOP|JF2tb=a6NhTii3dkIY* zG%-jTQT2ELQSBN;VuxME(hK~{{D7G8!^`h7aIP(y%Um7sjvK9zC}S57@T?J2MJGz9pA|1t#+1ua9%5A*v7c zd)=M|Ar%*5nX-qOJ3k{+d`D8L*wpjz3D|w5A90ND%VYV}^}UKrN#ep2bopCw zMDA(dnKe^?&eRNl%Xl6zfJf?})^x+D8pKypeWDDD%L_2wP|SN(n+z9X{VA^D2Njse zd7xpkz?WXtws`reI3YDoJm|0hr;&u#mmRg;r~=Jxb9s5Sm+FE!t*VmK`k-VI>~X^> z!tW+VB3p5+o`a3=AbAVXX`T=W?v(7k-9K0SmOIcGUX`!khBRUvo4(fIs(hRCY>au) z3cG&6*=BbZ)(@aJ)YzWO)~5rG%&eR?fxk6~sL%3_fS4aNwm}{pXQjuR!}*{9g~+i& z_o}U4;1crj3{7B8o85UmMA(gScNYmBZ$+2jLfA872l%nST`eRrj@s)It>|vDt=p*Uq+H69Y9K6Ux1W3RnVE z`hThm5<;b0ml+eTXJ;)6R*b@Rqb^6}rfep3t2pF-Fh*9o8^Y1aj~1Q9V)) ze3btX0)vDZmO^2Hc@T-t@NT-kXYJ{wwtK*QYZMl^i|*mQ3C=3lN#d?na4Ka;rY;uX z(yF`#&Wg<``oT}a%#4j_q^j!W}R4GAaT>>5b(B zd(U3##2dO?}Z=r~2-Hf9f#l zksz#of31O_Ij*%TaY_FDFP)!ZS<9oc^gs8DNHqEE})e40Z3J#k` zG7rg>MaL{7oBlSHfj(216+29A4mqdByB1>Ay59@WAb8B>`)Tle#{|a!%(i$COg79u zJx^^G4WbYcXDw#aL`Dv(uAjL(Uu#sHyN6${kp?~E6+ENd+AYd1t9scPz;x8mxohba zW1GEKXd2WqLapJPnkp$~w|drqGX3;t8-)pZ5It5wmJLUgN=xGeL1Ik|bI$an`JA}% zHNqmi<_d4z&7#XE*mrqvu2D)6fzX#@q@~4R78Wpq`JEOkvc z?X{Bt8=DWlh56vi3R_*7!Q7*f(pmuH{03jE+Fryl5T4B zp0@1VW!CJ{8ukcQ_&Ko6U4|;T=kF65N+b*KNR&X39Yz*GZ5%y{Nx-h6ZX||T?1WdK zBodD~Xp6}b(uoBFBxI))AIXT}DjpaJ8y=!w>py`WK*J@1tG`(5np>9svjyIy`@;kH zW&o?g^j#T}AZnHaE@*Z+i0&oIhZCSWTovjzel>#7@jQgvKvEr9abMk;#F2->miIcK z&Yp*|86j~iA*q0ad{_+JD8buEB#fRK#X=~d7G=LC4U|%yT_D11Eqz&^Te4-X4DUh3 z=hNa^rYAS zy*gmlTLNx!@|h@}bNp$pNjj&%&#Bh9WSeTF%hp{Vj`Kj?AK39YpBGn-ik@iZZT3(N zxPB%*LwBEFn#EM8O{~`LV-R?2S$m}*(%ZsG=Cx5=;GHuu)1~t=K%3F&bu?6|OxUV(S&*w*A|$E{Jh)H>r4 zNL~M&jakE$s+&DiRgbo~I2Un(u!ZfEPHv?59f_)WdO;LCM^qD3b$VZmC#1U4acj8H zJG>VH>8sjb0;TAWR+sXFC4+0Rl58AWyQ%d6rTGkRC1?*j@OV!Pv_ym`Wl5Zra56tx z7`+lAFvB>7M=lvp@&G6Qj|0_mrdO0r%xlUhOs4A2?gP3l8!NUC{6v-Ps8@(fOfQ{o3cu#rCMR0ClN-XCkf5ebGl< zIW}5uo{nv4tGQP71Yq+Ct|q@Z&x}M)5vcn zdO-0fv!4Mk&jrf;D-%htkW32a4)Dv{rMm(rAJzGHJ*btJ7@L_X%e*EU=>o@~Ja*)h zm82S-T{Of-5d;ivRSA#%HA!^(7Q}mQVW>$8y|4>SYV2X!%zy^Pk13;ut|O9%nGzWJ z-5rYx7GGi>yQYg-Mko8SNtEshR$$%7DJ>{mD`2)5C_+pQjSU<3Hl;o9m92I?87Umn zsFZ%}cx?-#`epNaej?V%!+9;S^+Hca8ci2-DE>3e;%wn0u}Ru5#wzbdS3oV0dXy?1 zx)sf@wT>=hAYGJi*LJNbw_i)1B^|cj%zKBl#DRMZ8nZPa8yWJ+t@!iN$|DUK4kO4- zdcVpYZ^PmXVQa7!N)|y?D7md-h~+1_OhmGBHRR9E2N%IiR69hFi4YnWra?;(A+E2{ zrGl=&Y?iU!<`@0P^22H9Ym#PaMn$IaW*0fl%&JO1`;wx9wv#6+qQqapr z8`)|OTTQ3oY)@Qc>?h=~xI^6+lfY%TWuhB+=AJ|y0)Wk7Z=DD6CwGifkppGa>zY9& zpb>sY4f-lT_DuA|5?j?X`mPumnFXm-(`H%q&%8Kr-`jL7Za2lV#JQHxYL#E}mL%gw zbQHX*!MISe9vl@sIlxjQa7KQ(x|etLWkQ1EQ3KzJ>{`y5&q2Dgp*J||eOc~gD0nF< z)NK_l2+l|%#3;THQD;DxWiXlYevLX~u}Z6#Vmel)N{2BCu0LH=qnvEcD6H3vrJCB} z5vL~B$|pIGhWyTU;Z{~p9%&=V<*DaDwq31Q(eBGJw=Bipq!%dV=$Ok`l-A1Ba+Zc6 zmys{G)`GY151ra5F&o_TE5?&{iN_ReU=j&H5{@9_uuf(f1@LlUcKh=4tUcqTq}`O@ z^jb(AV@yXXz@&l&VAP+iw?A2c*Ev^-3YKBbPOe3pQ(Pm6nFTF2EuxFilvr2`!jljj zfKshH{YZ&IKS0(X#aXbrakb~*Sm(uQCRFFFFg~|!BhQcoX|tHhL>#35Bu^RVGttwr z65LIq5HN9K*J_7dk0DTM{{*^Ox4=v7tP* za4fO)tZ0NC>B_mxu^u>=r7z!_5hbxzYXHucos9Twf;wKTJwg@2i zgBJosT|d>mv!T4YUGENq&RB5G12}{g+ z5mz+Ji2^PxFduk?jpE^KJ8H_XlIZr&1Nv)e?N~Z0Pc}w9WyiiyqXnxRRVA~PJJV>CAR8P52d<&;3RF0- z|7PHdxui5RsO&sUwPzwzta@WkUHe5!T^q>()SR*$)%U6Np)p_hgL{DJN{seiSeLpZ z_L!#XumKyO&hm*oq!}nYJfTIc12(EY)vP+Ld)jSeShKh0>e|NVKe>R@#$2GntZn4f zmQ*vmYFZPR#YyCaEF;yPewpt|Vurls;KOT~B|VYp9@mh(qeDo7V7*HC2)-`G-$G z-O!5C56NX}(qk!M*cGwUIw!EbTf=(|Max4V*vt1xje-MH-x^Gx59>P(8Yk8KzS5O; z>w_!}B3$0o58jBR$xSg1`r9f#I zTOFs;s^2T5R!%>TR@sK_y|kAVhtV~-!52?>qSOyi%_3QG0k&_c`Y(m5p@p^Xtc)*b zVPQz_G4!S_0{{@XKi*<;f?)rZ7So z_1s=u4%7S2oSL=wE)|&R<^19>Dj@g4i~wtRD9LR-VUmrx)=${E#k;tYvfal0^hJ9a z_U|gKn5plr-9JO&7#B-eyrRd1_c++1>{c-`8my3(ZH}m37kI@*XpE8@8O48t21>*A6S1Xuh;0v-Ohe#aH<6S&Di7A-a!w%2fc$b1 zCc#T>Yhu;9Z{=m(@*FiRHWs^-vdS@6$dE^d@(L0eS>To1mZbt0OhC!EMirZ?S4w${ zpgqi0N#EHlWLdL>?vhR01rzMEGsdSR`FW*e@5kSSb#xn)R(#XAz<&G*|8mMUcGP2K z^@gOo&@mp8hp`?$V2zyLXR<*HSKrc8L-Pw0D8lz-N-gKJfBfdS|LbJ>MxSwmAlEt1 zXBi70zn9-gPIvI{vvi;zk1Ts82yLtLccc1_CQg4!wT-3NV+F!Xc7F!}ud!`3-O zhY~bcIJRxu&Wml^wr$(CZQIF@+!FIkO ztgiK;%L%)1Z8p2p?R)<)T%|VK_r0Q;&KWV#o%xf6M_-g@feEe=Y@xUQg>8W8E1~aj zn+>ZzU)?aus76C$$T8EZ!R{*v(klR*k0Tvuik#hEGq_U`WjMMGr~#bpCi51|PWT~6 z3(_1}k)x+7U19zJtmK{VKNZ4ci_0p|i0gM)wa6ncG9xN>J9{+b*-Lm7pqQWTjy-i8 zktEiUrTE=`9crbob6#lqkkQIsG)dJooFYyd6gkA)8$8{hmEv6^G63JXv%cRQ} zfO^A-P(+NeKGX5LJii_ngj}*tNC+9OcVTO#Pov77qWYTZG7|gcy@y;eYI>^18VYC@MNi z?uRS9P`N+`A;iC|*uFdl$zCwZ^rN^Fipf@qDXSvl&N-88c+{YiDNEsyaRs_#&UV-W z)fEw3;|Dpn%S*}H@aq!g8pMJzwuav^DhyF`0Sd&^3u5S;R@dV5iU6xj!1GgD;=7aA z-n4>OO6cP9{7256y$*1{4MV>0UCBmuZ|XqC=V&j|S&{E@nG5>Jg66n0)DiYj^yz@? z)KOt4tx;3gX6DFUje}n#qlK)z#L>Mex=13K8d(Xc01*J^0Psq-%)0+kZ{3FF*?}8v zZN^;We!tbRTLY0enZLRMJxp)uqde_iuA@nMM$9g{?WC})xs9w!sR znh3s7u-97lI<#eQOOYzp2ytw&Hp4!qp%kIKOH@=5KKw%5)W-Eu9jqWSpmmiRHPRH6 zHXcMJTh)>}!Co2B3kctDxc`vSQJTNrD3BJ-Iqa|4&`X7-JRgRcME5`p-h&mgOgY)! zGNb;l=6x{PzggbVsv2x4_bqZC!L!l&^C*O3L)Ajd-yr!02~!N19Hh5@oDpp=F_6xu}8SCr!%xQT_FKNw)aT;kC52mfPLT67V9^ zF14_m9Id9!k|?wVnNi!1D7CV6F+y^g`c$F9`IxRuxziFZLNZcE1wMSi&bJZ@S7TIt zHcS%mZxPV*M67u77E&aUZ0if?3QbqJDugM5nTikesW=>SJ0v+Z6lDEzC?J2nm_7r7 z+nCJfqrPKHia!q0vp2fbtYnv`S!Cv76~QoR_LNUbxcW&RpU(7Y+xIyw1Sd4Rn!XXT zVTugL5TV_`?pvGn;OkF~r$36XIS<#h3+-akYoUbAsdv-AW%|IzUe-#n!dip_8#fQ< zx^I_@YnTGjrTI8E0>k=JR8TjkeL4Mi- zF(`@Ue47cs2u6d^4n5Vrb43~N$g0kGSdxt#c++)d z+-(L+X1(@bN{h+rC7La#09}LyK&70gXpt^()PZ`)y{1M0aHpO3S-U3*rHhUYu9ul_ zuw61OIYoC;o3>>cvThp^YkUWE-3;LQ--7z<{`}3p+w%c$H2s&cK%03+eVOSE6aF+M zW%6>ajx1foxgSGAYaq^5f_=hRN-e5L#QT#>;&sp@d#f?Df@ zt(GiUrBWxNKRrZyevO}&KY)&lfnZ0&Xov3p{pLx(4aZn-rFD2kJAZp6!tA}0hG>E_ zYRQQ}v;AZ#R7Wssg!mD7vOZ0G4bv&D>T}FO5;e>y47#g;0WgK-8xL+Eu)Ts3J}l`5 zLSFlw;&d+#C zh@R$OvH))MSU@a+8y)aA9&XG4HM=Hx*XRxc5jYEQuW1%g!I6n|2gF;z$7$@a9hUxP z9GVcEVJnJabQ@DerUs`(Psaq7qXOJTR8i}HzUF4}3-A@LDV+f_3v^g7kzBBgvc>#S z-8!?m%Ze5oHsoWMqVa;Iid-Vwy$NAP0@jM@7D>Ou8{fg(rfN#zP2Y)R`(Mm%-+7*0 zPX`6bQ9ED?qc^sjJ1ffB>>gDkwXxDRuABiZKWHm$yagGQscOL8BoyXHm$`T*SaQZp z6phwQ#>Z?AwS3(dib9Hjk|iP2m_d32X*|B?6Zw^eJu?2+y;gQ@cWjHqBW#`tccI-p zz%^Jf80_TPyWcNP#+x4gR1wE$aa>A8j3KZXvxtut5{Jbi!D zQB3$sy5+0(_qiMOE$h$NRH_$JtKAIj@su~0@83sNjd5RGFjJ@$aA#eLTg;G$<93a z-8dj{ZzZx{th?*<4SnDZ8CDG-f3AEWS$RvbJq!Jzw_JwjvJbh26;GK)L74JEyRG!A zJPQGv_&b22`WLkOQxTfl>@Tl&jnD1(?(cgax4W=~Lrf|T?*P>ujJ#K!dcUKk44qR1 z)Mb5qP$y~qG6+4Y-IR{xG#HKQzm6Dlw){TTysR2}nH&xcrwho6_7ej3HOm`126WB^C|#yElbo;pP-sl^Pz%5K~^))zf>?50}ksYm9xQ?1XE6Ln&nWqZ&Ox` zC_y{f$|;A@sv-+I=^P4gp#vhhs(2~asOUXat4hg^TSqkXUEHyCT0~je(8mXKz3&M9 zzt7QXYkg0oYSVZtt_?QLbG3bAE43c4JHPM$>{pmosZvT!CKa+!b)*3`3vP?pGf-Bj z#|IBUyjRwXc=I+m>cQ&;3-*v)nFt0B|6&5$?2N(RbX`2bAnus>wyK-`fr1xG^MFANyp4(V<0w}xszfd)xwrO!o}AL zF(acanfUYdcJg@N1jEGmh31pGkd11EO=>#)-k17stpRr@=A5^FHh9Q(_%hT@t)vFw zBrw#fcPCjPE^JVV3F-&@1$wBFB^DDkOXMz-bS4@tf9mH+`bJ`f9`PhGcYDKFiX~sx9r@++M3q#rwD=MFI;HnK4{@t?03hM~cLD99h-HoYUw+`De-x5KN zv?1)T4p%@}oX$?sd9qM2bw%vEl$2Zn5f+4!cp)GlhAYn7^*3Kix{4iNR&bt{XQK&Q zvkzLv3{s~e-x3<`Z=B(xz|kn`R6dWCNpfE}$}-d_IJRaK>QSUN6qT<@@R8&+nQSaP{i?TBL zRco~{PpNi=)dJ`-+D(Ya5>?h7E;NW5q)+3VrK1vE6$A%Ipk5DlM$9PAQE1n+YMRNX~zyBnp|^r|6j94cL2F@87K^YJ$`?@=rb>2lk5BQ&o@ z(xJ(DBOr0-b`Et9x&02MV*X_%Sv`7Vse;rhw5_1Q-ifF195(W)#l;zw>#3^(z z>{xJ@`5KASvUR_AiDP5E>eIv*-U6#>(Knm26V}8i*rdjd#wxX;x_NrYM@k2ZQ`+IN z@D~$NC*fzLBr0L?@sL}Vh_FSlJcTxE_prVapO{^2o94o|BfoBEoRI@GY06|2X8fOz zsg0ovNCL4m(uR#K(>H@#7m10~!#Be)Pm*>}g@(^J45IWB?_cxV@2>v+b$$H7EV~|& z@bxJq$?OxEB1~Mmi3+vjY?mx`ra@#SZEqb=y(P^n3r23dSnV9?NCVQ#WVy)=*}25! z`paJ3oeAdDtP!K*hnw6{zj1BIRA$GUW@Jqw$mh(v_M1R^=w9y?w)VN#G`WAI5hxx; zJ6=06MC1c{{?v_QE{jAd*|H73bavzv)+8je)W5676C1{m%-s$Q?=UPyC=b?g^_q0| zXs$?Dgu4C`5lhrG>y4WmrQkAewTri99O8`;W3_(|Tt7vFOadv%eg}5=9{H|X&cS0| zayXi9r^{_esYr<`v8dNRVApFdQNuVz*c^#31cUce0QtCk!1~NWiW)=cKRptMJbhu^ zpkQs{S1o8vT^L7j2v}`e9Zq69P0QN&mmRy012t={-0h(W2}>$RtEA+NAmBZptIguG6&w z*{gHIt81q5Z^h0T<=_E3$6T$a?nL7AzW9$n2j|!Kp01h|cv=^YQNiuN!-A5wn*uU6swCV?x*`@#5Hfo@ z-#ZN5pu1J=Js`LHQ3uT@dy5&m;4T+9J7Ap6Xkhs>Llec%mVmir=g!CiB?K9D~$hDCf4lGv<0yjh~)L1r1GuKqH%EZY-2IDzaTt8~jqDD4)F`8f?IubcyM-J(^(>-jBHi!af zX+;@R!)Dka``vb+0h5y7g=}D{WeVxgE9@i8-rm{v=^JA$ zY%nLbeh!h&KenGzHfcgpfnK9cLu8ICA`UZ}L|uZdFvT2(&$T&S4PVZ$Q*qLvr#F{S zo01w=BRk%3n^rQLw7RiATqOU!Il=p}MF3(C=bxVtF>)M7bw80S#RE7->HIzVEpr>q zWXpM=v!7NyCEHwX0WQ)EIav;y4~AxPYJD8J*R%;#e}d(C@UBLRw80Wa_sX>vxDJr5 zYn`XEsv^G*S9jU?T9q7NisI5dz0Y)(MIt^)ftcPNrd2wet69)y-|JaxotL=XU=ww2 znfF+StkCYru*LDT2uEDt&}C1%t2z>DWxRj8PUk@sNAiUHl7J>*vdc8*WDSuU!SE%l zgE<9n1iGc_w@))iEN-JgAcOtwj8v2kXuac)LYZDqtQ|)Sz~u-KZ=XDkv=@`-5`lG~ z?9>g+ijy(~1$Gb+WXkgZheB&vQ8)V|-FuEj*)q)fhe^t=!<-x)or>~y{jpXuy`>sr zTJQ|yDYf5u9!LctmsACpZlRM2bx`GoV#19$$5Cx{fsZyNi!wJgQU$9FcAQgXQBf?{ zz)rx$!-A)r`4l_pTvV?9Y8aHPia>@f!((WpUZfZ%A{~M5F5Mm(Azv2++h7a+ud5T# zRXaZ*$N-USZ4}2lCRQVpEMsT#0We$ohydj=-ry`c6}__gCK}PQ#c(m=Y_Vi?_}OK& z2zKuI^7w;7eUq+^abPrE9YriW@rJterh2jSlmaoj_)Cw$P173U1eOQ3qOeVHC;;%C zC1b=O!EUJ3JW<)KfhLS(X@O2t zA%J)Xe_bQlXuGH)4+UiFA<7Yx&xB>*SXLuESEMzB_TBTeYC{I*W|M&iP2jIHQBpVh z`y8Vh?+5Sh-oDr22}rel+|*wD{&@O@ww#v&95Z&&y85|41nKE^J;AZtSRID-ypod# z4K^XpCPd7!TffK!k-q`Ly9qs*gL}Ir=G3hOM`FE4=UL35zaC5E2*ttVz?@qK7 zWekjN^&WTX-_25>sAD2LVt_D~NaQlc#C9&d>eu<=7ALa=-{cIr6_c#d=L~7J2csfp zEZ)AM*Ds=GE-S6@u^9X!dVrvv*Xo(-yE#_@>V;V~L^hVl!IW!uIYL@kmDl^1H^`%4 zyBi@T#TfMFlk@`l<)Hc1Fi7jmT*gkmQn*bH$*%NCLcQnl+HA?(?Auj&<&gSEOlA^+ z3uLnWR7wjB-`(H-(m|u7TuNL&(W_IlH0jCT0nR#g_R7fxBaZj5>489|CVWK7MpQf5 zSDkmzzWP9Kp=jq5EaIh~&qoru=47cjZg|x!Yw5Pg<35^L@poDGV_l?hz%kc1!N@v| za8}&37lrAOhK21n&8uKY3&|U4bBGc%r&sf*>?j06lalIgViPhOiBz|}G`FU*KAY~F z`J6773}!EmA?XU)Uh42W!rZatqs`=kZ>-%Li4t#0OvM8Ix#2hUk+((=meS%wjMF^O z#rkw+kC@~LGG?CnOslBS`@`fcFSNou;H#A zGqosy*kuIG09$$BN(D!%ad9sk?tM5gpecn|v55len_HL4kT#ZI^L_s#o7~<$=c062 z-TgRaJgc;W!dS~Yi_svOT=h7o7Asnp2Tnf7j^DO!3jSli`0zW&N(=0P*L@9>a$LuN zLLBa2;eTvv>yf3y?BiO*zoAGTU8M0YVia6OYzLYXz%u<&f1Y!INuFzK>wO9PdfWxG z6YQvt;N$rhnPHIghogI2BGo%Bk{cqZC9J&>1Bukt^mW!Cdr!}xi`~d zlvguMwkKjC8rNt>Fdo?&;-8q&tZx%>1L?sh7p9&EC$d$Y=g$}EFqGvwC*U5bT^dMT zd^uyNO>fzDWq9`;l9pNo*J|KL6xB(aFu)U1#;Qs zFOIVIH`jA9hTf(!qy&hCgXfKPef?Lr0x!B&^*~r&xl!ha*UNz&u*HXqx8Hf-x9PXj zeDNa(bnU?)(U?^V(f5;7(>3cJS(e#=o~DhqRW9Aa+`PTN6}X%k_3bxMO%xB9vE*B| zc)=--?h-*7$s~!5t>;oWtP(CUQmV6LgvEa1svK~C!VR-G3gbO&qChOU&HuxO zH!1>bCDBmEPm5HP`#fu&=Di~J31^#{0GdeBOR*NaupV97=D;$d>>`kjTJQ-I)G$mh zEEeDKlN;RNY1@y4l$mEiijfr-skQZ0$g;vYF;$yxSeC=shNWNeZ<(?qO! zR)K2MWxF|{mebv#t}85EAw|d09Y3AIJs)Kwmtbhp=ahmm2>)ndz&Y)>?SLgi9{{#> z4+_f0=1evry6(b~1SfZ|g&=hXgeBO2(CFP1bM~v8kT-9LR9AZR-MH2cJ;YeUJ5xj% z${ynn6V5aY#v|&V6nj|eCW$*@6;{Myf@rwf#-y{JRWiP{xjB%JH#N@)+k@(uKtd|U z#aw5{GkB>BNoA6Y9fqA#b#$lf=0;J z3@0amp~njS-pCa*kxfQ&LnLvEQoW)JqQ?OgK4O94y|VL1SkNdUHLFroB9K~t5=D6# z((iKC!HybQL8E9Gpe{xFozTX6n4%e`w8+DrnFtc@)9WEcksuDB6dKyvon)c;D*m6Z zD4~5dcw{3w$L*kkOLpcui^}p{iyIr+c^;`^2%0ygStrVWNlw?9C&yn-5Yb(}1qdsM z)ROE=0XRimvCP8Ed)F8oH%ii~tvxS>S|zfba-n?RppuspinSfKa{;8i_wN{>+Vh>_ zQ%3xed$A#=Y$3soS67-q^O+ZZ5^AI&K8daKV3dM6E|)OjLQ&?OyP+)UI;rWcc0;!# zj^x9ge4}Kt_m(feww#NC0Qo zA4Ph4I`{e)u)~PI5cGH_j0{pIseMzNsXgOM)$;l`f7mAG9mt-ISLecr!ph`zhf$p% z@PC8PNQRjCT-Mb#OF>KgKMMR60_hOaWff7I1_bKkUt1N3 ze1xQ_Avy+i{JAZwxVe1iZP*(*J?4lO6bL%BhTg=%L4nd z%(!jv*ain~>Y~L7QNOo&1!EpB>(NYXJGT znG|hM^@=5!6kKa%pJ&jY4H07L)7kkzrEV#-_P1o7DF2?%Mxda?D;0SpQbI#FruABq$uvP?JD|2l>@WD=();gVAGoW!LO2I^$ zw;(et**lK3b#63Yl5#;GDTd@aC#y2W4QTv^YY83G9vR1I4yDo_g2gufD_&%AMmy*% zr7cZKom#1**-3!iG^5VpW`C$TLcIFg<^4+rce8$qQ>VzVr)09kk&@@AoY#n!ZD<5u zOtPubCHvJQ@G8PIx&;S#;s7AyfAeL!0^C~YZd?t1|9GhH=4a2uiJKqg>fG)1a%hoo zh&KzmIe5-);4toRhH$rXhpFLz#u+zE6|NBm2@_bKM`$eS+f3^)8qlDyxT}TEd!Hcc z*AuJ7UO&UWPn92#{gR$HNW~9)DzPbn2Z?FWApCHl__Ht!vL(j|4r+~rs?HDY-;~5s zxe&nKLx`Cd349p9y}{9u+lah>MHK4XGWNls>~jni#E?ChufiV&NT%-Yfx|}~E34Rh zZU$!BiC%k~L#E8BK9=nG6lfi6px*$FQd95r)KAM?xhV2%UK=&S$d{kHPib-{4N@a> z$I#mDg!%Y#51qCROJQ_i&$P&zdvOM>pasExJ=aAlp*xSA%80=1K+g0nfkp>B0Wt#6 z=zZDrXYW+tn^Zdyyi_t+d_Ru(`bcE&dtatbI?AHyD>pZalUu?%dj zOzv7(Q3U!*m>h0A5MOoXk^J}=m9IF$vOaZ^Y%V`}n?KH%x6ynJw2ETzx-*i_B{uGw zS>8PG8|D@!6SOLQ+{=`ve%!ldrGp~!gNS_!mBy5#AbWep;)8o?ZTGtDELr=)jrqW= zr=Co))USz9%1i847aktG2tVv|ba4J|`)0Ghg6HQ9(2HN~@Dm?Cv`&o3CIBhQdfviH zAEze>)#J_6oq_vP9dNuC(e~6&qfE?QSITNY`j`<}x?6fs!+9|~GzdPZ=he*E{SPl@ zH>PiV#3u)Zn$WtXsa<$(Q9U7eqN*ouwd`)6fjT3Csr`(*%ewxk&~bDF&8=fO3N)>g zoxL_i-}WYUgcXSBXVB{HZtw8h;$qybE!Mp}c7StSsJUl$i9$+YzeCVHCF~26r6ef>+`{@&S$!PnAkkq)?S#`7h zNe*uMjvnLniE%{{M1O8?_AmPMHnFgxp#Iq)VCjJV{?4@*sxi^Y@s#P?hX65u|IVPj zb!QC5o}5eJBRH6YKOLo)K$OwIuVOeKP4ZwL&pk%+)Dn;a)8A6@qT^JDj6`@`kx-IB zps?LzXQ?k%?FQpT<5XZF$o8T(@re?Yj3hK6D6@AXO;Vm$3v*MK@R+pIPZ`j@EeCuXedk8K1lu^?O|1PI@@~Ot_CFLcJJOBCi^CD*!e|DX7)iltJAJj33B13B)sxTo}9MYGea4<%sP(tsY+{$NAMEZ zf-eva12LTg$Qgbkg9W>gL$y-%hmgU9V=TaAGEBSgRZicI&g6o6m^mw&<}GQDoDkAZ z3bb?HoSUmKU3KvV9EKxK@t-JOM{_gLeW9Hh9rhet3u`R&>l;@Fmk>krm%j|rE zCVQ+utd9l(vb}$N@Bj4Yhfw|6qBHPf<@j)yemqj^ldwP~JHLhfalrb=Hb7(>8|+yq z9xcL^j|xlzawQQASWDX64K?E%BXg*ym*p7ttS$YhBoF#Ua>;_CK(PP370@mO{CjIb zs0SOD?O)Hs*&nfQsR$Njj50z-7yTVn8Lj>CeO1 z*KMce-0*+1zaa3W-%vKGWRh?TV0NEy?4#&#o=ZMAfyI2Q!@^TkmCJ1EiqHmrb>@pp z_i@LwnXPNOeW+3z3-p6!`&1I@0%R#?a&D0N#kQx0kTQ>k;~r_qCAQNLZGL#$xy`L% z3xh!55CGSR8VCo#3a>tl-6jYt7~M$;YaZvZT6RuXqPB%4f+zk<*Rh8o|Nc0pV&L1f z`t$zD`p)(!{*uZhGvWLD+#lGh&C6P6*Y7%v;67UPS#uQ5;*R}DR=#K0`AP7pD_z-z zhGOtsrCk#qO-gIe)7bHztDBu?&`TzrP3}ut)CcAWfzqb~6wgi$9nD;xD?Z3eH(e%> zl*z0WhHT3e$6W)*Sj{j9i7s)uq!&`C5Gat@?-02ZL7V3Geiv3xf)|1(UrOPQNW;Ca zz-|N@3N_*XK>fSyR;rONdn!Igm$F%()$XIu$H>x zTOheVW#B6tj`RlC0Mm1{YDbdhj91G5Q*&eBl4l82`n+$ zmpLF@O_$jf!AvyVWPtLpJf4&nEo7i^5{VD+#3^N6`@IB%=k~MiU2Us3qKYX$4@o<2 zm_d&)^zYf@GUXx8Y5EmVX^jsGq;CYnr zBeOTsU#xNa30ZvXgDr{D0^TX#(CLXkaL+IuGF6Yzm(Q^&56EW~az~Rci*=v}idX{+ zW4)@QYF$Jnsr6pt&Nfth9gi)`O-WfOVs6$aOyvYgb3 z1!HB&g~qseg^`@d66%-s3GKL=CPa{T4jp4)FZdIpEgl9FG{%D+pzy3Uy0gOM=}mJ- zM7am{uxy5`_(^d`Ci_(6s1Uq#)EfIgRga0N=V(()?Y_9|nB&%1+b6oX!U5F=5&uqg zO}(3MFk2icf>cT5s?|E-##s&AgC{mr=aX8m)Mpb_*JL!1_%zXE-P}8PE74iID^E_G zgV(r)ds%Dy=%iFjXur`<#}mW9>{`E68cK3oG6H?yUYt34DQ~6UUjs!vC_#Z)ZUWLj zGS;UYob@P-H0byu7&n4WO(~*n}9L?2cXnI9j$nWLhZ9~TUu1|Fjtqo9$J_BRYW+7}`poKT;b5ZNe< z|GkW!LP4NWS>$h>6AM(yAx|vn-#Yko;0%fWALRNfsV1>LZ~%Z6VgLZ_|2F3TqdS>6 z{u}bO#-;6HE7DJ0*5}lmb?cXc#60&Il=a3! z;}jxnjfCd_bPja4qlZJL$W*Obh_5S5)J$`U7w1vV%rfMjE(ah?ZeUvhEnxr{#x zOQ^sU!)xkq`9eYE1QS|H8EnYUMAb+Yg&}t13_8E}J4=`6IyJT5UffL zIw@?Fb>f?DJ9pQ3Nm_Q1Mtd#{`cg%%^)Og}p1I%V<-3zOxbPPXi5UYIIL%L-h?yKt zg4%TBgD{M2zk2CyIV0kMx<`Ly*_MfJI}(JQrUUts^H>3K#*ei!A>uWvqJ-~byS zMR6-Z1SE8z4P(W+Gp)}SXqNzvnx_Sjq{S=w7rIi}YTUxDhbbpeXQ&(tF$yBZD>N|$ zYd-;Zkf+Hbk02+uDFd_^$(u?1pzypM zB7L|spm3&S{+vE3pI1aTsU;!M$7=Z{Vmr7#s5=PfFp38gonO;Jah!RrlI@(|45unx z=lXirj^qexW7!&o{bD?|8sosD=7;C>Y?oV=b9`~O;FJ@WipTZDSI+5bxQosWpOVG1 zsFksb)++S_RILa40W?d0M_Doe<8Ie!uelYiQmGZ5>ZS+-dUfOVWoHrb{TicS*oiYs zY!@Q~T-W?v(~@D_OcCm1wd$N1+TUA&>aOP_Y}W2v9Z31n-_z51|3 zcVrU>#Nn&}+e^_mB+|b^$GSA{IxFIl#X82vD5QFW53Z~qyQcA&=>FKdN7(2qWTxS)=Ab z1+Rk_45d{xEVm2zubq0u0VhPeh&k9amzt5pd0w_`!Hm7ym4X}rSQO>Y7U?q@$7ji zcGo;HoII}qxM}3ztlfr%>Cmomc;NG1S|a)eL9D*Hb5Wryk$jEoV-;J=3pc(d#SPM! z-~dRe3g!PzO-krQIm9LuxR@6UYyblY7fYzrF6I_QFE{Ok6kjeTEC=|P+QH{rdXbYp zzI!UMk!Q3YPKfzz+-?^lrdY%M=c%hP%W#&b0-naF2&~!lp2OJZ-z)ak8M9s$hM~!` zJXqgH?e>W1<|Px&UM%r6_K|TiRgv+2Svmt0ocis34lQ2nqk+5lS9;aHBZ&U*&#i;p z}p5}Yg1T1jwL(j9O3GbROM;E7FDmsg&bLki!X#R^8M+4@Q<_2TkIHzUVHhy{*wdVk2)GA)FwX=u*EzT;5MQ}Jm01MHRh zWMTyO%^%u0LX2h?Y-bVbx=?n^F_RF(Z_5`;Jxed-Iqe0{*R~)f6Y2?$nuNV@>rK-X z$kf^cL8NWE@O9)o6!5xhmN+n>nwe-6#nb>0>!q%hwSa+{o@sh!enZOi_4z}EiNG^2 zi{AYL@kBT{-WrWk(G_y~6EYxrg6J;+IaV|a(M|n$SQvOwEX{<&564A0y<807_8y)3 zlU>&U!dI`2`xeBObS`UDRJXanzd)-H0ITu0iJJ|TOmiJ>&+=U3EAYzR3e*emS5731 z6srXb;8Pj2 z#v2PTVBFKQI%^b8XK`Gt3_zHO=h+Z5AA~C+(2oLMu)G?#6aMuuI99tXf{PK}`o5;Q ziS-b%uiEjsjdJTNHr!AcGwnOg9N=MAThtqGGrul*ip&%}lbuAF2>$#;W#{DPxa^;T zGbKVI*JIWdrIT?drY>8ykX1;Iy?4DD%NPwCt~NeNR;CkiD;T3qp|>&k zVbK$KFi7uP}m#kpF>fh+oI6R8nT%&CINCm5@9-`h={O$_!C2b*|E z)s98ljwSk3gHt$mT9dM{#j5^6O?DhAE2*rXM~rkv`MJJg5dY{v8RyzcvHIBwQE5Qw^b z=QQ@~Ild|=$VJed%p9GMeEZ>+SxA59-KP%s1Ih(Kzc8nu)`GGS1QtLI=G8M~(mB2z zH;qXsh;&y4yX>ScC8B)Of%6LZ?UO(s9QC&-a}5tK^y)NbX{O;N&%N~N!23AG4?V>f zOS>iPzI(&N0OHDG{frrL-F)4MUB`gu``lR`-7g^&Cq!CoMc-zQ7`ld|(KYGMGIX|s zZpF4^!qGPGRTv;RE^^o0yGHy7f>+tl*6vqIPz?mCNG;(JYpq;A<8*`Ilbu~?99a#D zXcfAhV~MafIy#yIqX46;T*v6a)%7fLE7~tMbz;CghDld|CP|@KGhnk&V4BkQm*dq6gq{dqg@xD#$ zra+?qjf)z2&O_l7>=r(75GHEq%fK?pNM`=Sp+Khwb-@6xPW>AAzuYn zo_(X6P5OLO!4l@kZeACjYqX>o2cyhA7enL*6H8248b|9AG^g2@jMdQPGmAB4p4fs+ zVQN=ySPw8LKjxgmIXVwB*-)kRJ1Ne-q3^{qj*Sv7mqE8Ima>NRB6Bs!r${B4ZCK$+ zD&2#2n5l4&u>}x100g5Ake$nQLX{Cq)+W{w@&><^#t?SXq0cgom0uTTnMHNRwyYq; zE)-4LxZWw|0@Jlfh?JBduzHC+j)#2Y_OP{iBQPUCxmck~QZyCsZ9GjuoAi4DkP!p% zo-kf==7XY;Rt^gH0m7NI(q$e(IFPD1l&WG0rIx3*hp|R&mU{RDxU^>7aVf;5EcgJvk+XkzLv(D1VW~o}zMY`hEHmCM&E5PIxmRY8)b-Q`3|85FvS ztpfFo=bUf$NP_x%Yvz{jIi*XnwO~}2IOQiX-{39JpKfn$f18L&Grm7Yt!L7FE=Ma; zZA)}?BaMjh?>s8Fwq|QfM3};aKVSNF0LU9Rl8z8 zYOx-;koty~@1Y$!6(9y$DHzbH{@|}*Ha(7T$kN8>5C}E49Ygf{i~t42-%t6sj!KXe zgLG)pOqe|`5q)%(C5)Ke&>zr?wj8*>L&D^8+;nims^-3BcIhY{nKth-y*7RDe`IfV z{Ql$XwatEuK!_XwfVB_+0RO+Kn1!vgiKD%v$-n16HCy)CblenAJnQNOBIfhwmg(R2 zIf=l|#B961S&7|rUAyQ3i30*m$PY#ow+B#U5-Wh`>~rX~epF1WhUTx_bTUjsOqIx| zqOPK%X8w0wiafj@X8r#0Qk$N1QOnh#nRss1R5js}(Y1+uxAA!KdAS~sI&x`RZ(E+e zW_$PhWq<$KyzS!);`5WHTiI+3O}j3&h_YF(mXnp0le@R*vXFYqqRC>*V88sg15>@s zI&zucVHr1Rs!S5;s9r%;y-2i4`cibA=Ja@ee>ggNXtka!_V?3iqDpdUqOHATs;E+F zxkmENApO-Pi!#lzO?v3nCf{_8V^9`Sw(*L(=270au7YzOVonNi=!S zHxQhJZyQ!EP+M|&oFi@3Wa^~*7v!3KCBHP)i6xh4&;>9YmszAq4Eq->+j2pIG`C3U zhv-N5=ceyOvLYS!?xMah4%y}jFxUi&b;;3AlhFF{QENFS8f4_=&}f~e8h<>t?XsHW zma9ZfpF<-cb_SkvWxxlS?v+`)OxO5@_nzYYtKGi1E&{I=o_3gB?G zwbsIlGR7vdnewsj+Sd3Uzefx8(o(}!>)Y2k@$V`6v-7~?du_(ZQ0hZsViLR}^`#yVhy`@fXuZ{k+$2w zhtcL2tf{DeLOKUZiNmb`Oa2ikZaOh7u$9`=eBlYc3u_jsC`mvv{cUIre`}Jj? zZuhT+q>9S!DNDBdDt&wN{FE*pu24IZz~@o85#=2iOnu?quo2 zv(GZyC2Dj9-P;OpBI)V0rP4CLvPM^vJYWCo>IH1u&-vGtJ z+*{TtTDn98>wXOgOIGgkvFFh8s2Zd+tRJ~4vD8304X3e9r@i8e&N)g!s6XiKhD)O~ zzDtsvS)BjbBJrx1q?|Ii!N>+t80mt=2Y$yNYWWJ` zrPXOqjqNM-RtAQ>m>H#m?vNX=#~DOC6=#dh<~46pB)dN7TB;iY|zE7Y%}0 zPz#3)AG$W?voX}Elp%Cs;~orw*C?Hy>9oH~S+0INDVq%!j7^RgS9aiw2zdau;z88& zo=Ve@d%}Nt;oNnqYte_58{efGGME%d;8EVpm&C}Mo^b=UXq=_SCeW}_!=@h7- zGXp-jj_`8`A-@QgDuTh>XKJUcNLXNNHG{zBZ6`qu5-f&BT76&{h)vrurWcx+5iI}e zr&dt;`hJx6^5m4N87ZmMpjThN=+zgfG!&Eu98&XgvO3DaMwTR;jrsc~B9x&xLCH&0 z8mS2cTue1OhR$NLpqE+9(M9hcCNYN&eRKmlqeZhv-a+`E*_xL2S6-PyTu#vE?522? z8;rx7TP4{vZ;j&r1sN4+ncjv~CiW)bnME0^$BqB(Fg&XC1&iA#R51*+7-Y(-ANPow z2np;5ZaVt?lLX5QlEC1#E|OBq;VyF|{>dKYJtsmdg$Efy`^J28lv z(CSDhTg?vz!sA1mBONxUOQq}RUsdVo4E+XX$RqNY%hVJ-#_#Oj)S<6LtGEt2Nbv0M z1^?DgGsFlg(aj%mDLolIM8b8AlI$PIfm*gE0~jGufu}~ZBAZFFfhHEV7c^&@H&2DJ z+zVaiCqtg=yIo;uZ-4xYDsz!*xN5_s*}r0}K%3T%ObmLh>XwC~uBinAj(#E&qE)8D zMc)N^6=siY%0CdT-yxz%uTPpDndk`=Nn~4ZKiO2;DQvzqz+O&dQR6qoyq>W` zp;Bp9T7IX8W3#~^y3|}kV-KJ%8+ci{{&3L>azumE0s> zKbo!Oo=ef8H_ZTopH%*sKC+#)W6fzc! z+;(7(TCKAniE;yK)`Sekje9ya@H1|i*O(6Oly1mN4&59l4iB)6d==_koz_mec|E%@ zeNMS!*LL8~_rq~v#$xSVHQfrk#^O$Qb`i&(i58FhfO6(fich7~L~2(DR9+Q#CA{oL3M9feB#ou*gG|4K%GOwJPA1&i03e+p`TbkD8~7KY#f287iccR7l~Fqwco1P22U4@%vZ5yzL!eYYEh!7s-zo|MAP)DR9Yd-d3b+ zz{*~u=-d{ttyz~3H8Nya%hmigxo*qi4wNU9hM5aVAo1qy=hvSuK3)z@wrmhMUz+xI z>E{o>y#4srbyP-)fS4rjBC) zKDlxn$Z=~;CKKBA&!>)Q^q?IDVOX=tu&6#@M?uwPep}A((8v$32%XG%Y8gH@L~WzN z!09_a?fUI4_!`)2Zg3tsEl8nKEnS=r7;&(eGUcT3-w2e84j7C#q0T2(ZHtx~Y4*|G zl{O})$Ho8{Gz2}+wf+DKsoGzrVL{WOVuSnqk%QRyVDTkku9}+M7xfMlPhrIxkJGR} z#@^#}rrUSha+OPKPKfR^Pz}+vEp+*9abM<-u?0m-=LzvXY=1+Acz7K>PfPA!ZZQSf zTYkK3RWQfU6&FVhR<2ukb_OuX0Q-3NbpCk%6fwH$n(oBY7|N+X zYL@%<_6KL5nmd-;D#M0UI4fXgZPURH5p{llaLjHuVqp-AfN(emJYm}cy1S(E)ER(G zdmpU}Zl>C}Qm!>I&dl@mzS9&uIaYeeX*I-VB^;qW9!HDgJ~wnD^b~do*@AX@Zs_@d z?Y`S>He~G1i|fj9T%hkpTXI}F_{4x%iG;TunODj$pl-qT?|M%gM@0{?jbw{1nj~DG zhIk(A($IQr-`i0D9H%~RRC(*NjAFeX%}FvprT_~CTdV$G?D5MJhksBJfr#%Vf%dXu z0UVy~Sba^PIkuXs2Ijx5g}Dn}`g%POeGo*AaMq!e%Q!p(r=5E1&UZ|dG(lb^vc19z z?`6dn)+korqRF0=z?(g=_36=L9rsCy1vDT`NiZt8vPkDjGZ6w5mxN>YH6%ylq<`6t zyNgK^wYS8aS=PEt--^Q}+$+(T^H~r+CNo zYnMP!0q<^s#VrEb&?FEsZp!|mB~~oX=t@BgmQS}M%}WAk^HiCB`TaPy2XQ!P*dtX(8{B9^EK##LL=an9p8XRo+*i*PaUYC z&^(8XoEDX*y$rGX3RMoSaB_WcSppNm$$ZA6$w5c=~xyaXN{hfU+2N5=*srq}FcOIFC{Q$Hj-j=AZtVm^_5rJ@ce!I5a@AB?meM42 zj)2|Q9w=lm?h{k|1?>AbiSR#l3Mm|5CT1ILCuHt^c4nkz2=j2j1< zA_uIY#E}gV86z4PlyAdtO#9CYF$`L@y>w~WfH#l5{<$n7fc+!-VwqmM+h$h-rMA`0 zO^I@)Ep<#0g?6o)1s<q^sWw--9%E$AIVo+eT9$hWXQj9G{GI=LTTty|V%1ioXC&3~JIEU7M}S z7UTy$>qagZD%g!fVBVY=)3ZWVsVaX7!j-gb?m)aofUs}V>w~XZ)^XvnsOjh$WP#0+ z59>Md++s0-RfwKV6yLyiHR<}sN5a%_%J?4JD}(NS?W4Gg_S}?J-PAv`uH0_biG!fG zlY~4H&hm+HQ^5aoyz8%NQ(o_}yU-B)x5Ul*vT8wT!69+_PL1V!NCVj|hqH=98zU+) z4vEwmEp6JN2o?wCAI`nvM`+NeuX(~~w)VDzCd6u=pf3cvR?<+zN&Dt@lX%02HG9iF zKz;I|XxA85Uah?oRilT_O5KbPSwYXTP`w<7rYk(_lqt|7^}oxhp=)jfZG)6{08~0t z<*q=@0#RPF?VSv2t4&=ZKZa@Ams{yIw5vuDOpcZJ`3A(~7Wx?&OdkZnN9!=zKXg-% zgd;p{b4Nb386%(+V@nfi3;~NtvKrHrtZ*`%!O_aGq7(Pv1IETf1#D*;Ot1DRaRIx5 z)Uk;<#vYGi@vPSsZ|x>VE}l~t^smUQyJM^fWd!XScU$Njj=9zQz3H|U<&IQExe@LC zwyn8rOJmeGdlPTJ>P0M%{f(ky8+2?4edcT=RA*+9yJ@_Rv;e2;hMA%X$zNNo*)#0Y z_VzqUGaDis+IxVk!=_cu=z^cgzPe{GNDm#o=FC;h+QzI*&xzp-wl?+G4vy^EknPlp zeqT{ifb6-EVT^1|lnf3w;|NuGye&NIqhL(^i5#RvZSy!lb}teFhG*zseZCB0A|iT` z-OkTsi4) zU_2cFa_X5FkC+Xqsp}c|q3B8+{3|*7XtB-NVq5HB{h~hzGa@9#Vu6Q)w$(jIvn>eo ze}OsaI@I@0fu-Rz$?p6)7Ziy8d?l9E?+cCYk zd3kIE5y2DJ{`ooSGsBIkbNpM(vs>u(}jqtPa(-2-Mw4OKDfAf(a7z;h6 z(ZMj_#vn9I$tZA2c0qy_roQ9RoduXWLzTWI&2C}OddCJm#HtUGEAiW1|KE5FfTItU zbdslT@PpFLqfeLfI}Y9fQubBCRJ*tx$1HAOt@m5LI#x?m)&`c}cpfPsXnY!nzbN&{s$$UQ#UUaJj0)z>?5?K6uzeAKE{SmKHZS`QZ}(X+cT zJE);>r#~`u$CzdOfX08Z&^zAFTeq-Xa{Qk5jbj>c_WvRN92kmA--!$Li)m|>AEs}> zG{CIayE1r**gyw$G&%-@x@kJ1;5cN?;j~Ty0tGR|Q{l>I__w!l=&FU>D5AsXj1diU ztaxHzk3)lp{`h2P8;co9cp{x3Db)?LFMfUiS+7>|hxSR^ze_ERwNlRlCGG8(^GV(8 z6hLTu*hKG}9m2b5Xq(L~0E1xvnK9xt*!hxG-mxu&Ze$!7Lv;A-@uptgdSkz-HyqJ?{1=q%sOC!s(<|#;qV?cU~JACe} z>|q;;3k(;|N>TzMp$+eGYH+laD?MZ8QILPNy+{gOK zKc?E>S&yLNo$f8J)f2&qtnYPrK!w0M#N2#iHxI)VZT&*$kzh&~oYwjt%fO6$j7<4m zI-`;g`oK!Rj0i=7RM1qHdIZ9NhU0C;@9o}`@ErO=ELz<+PcFuf3Lt|g2 z`CtqMdPpjAa0hQ$Zs)f?IrOJG9T=^UK>80Ih_rip#7Oo44A<{>cN3L7*az>Cj$RVo zp%?^IukS1LEJpB=M8qdO4v)kf`BSHpL!dZD0~c;JXC#OxBzxJZ?@Bfyxc$_`ZkXPW zn70iLF~xmR5onu(i*mZzuuZ_Rf_*Gn&Q=YDtj%?jTun!?Mede+^vzI$EdT<-0y8O) z$yNrE{QbIXrCdPpY%p2j6fv0odME14{8B^4!4mV=^ z_{o-JA7%h#)1Sr0_Wax|U$-Tf26e6^RSylRBc#hnpq2uBIC~u*FVc|VS7iK-SUbb3 zWoViG9v;G;k|9GyKTkm$gL-aufBnYqWwj<7pvBv1+YR8#ym01dF&gioK@AWFn;gPNpg`CD|PAz^sRll%`!LjQCF>im9=DdMHc}( ze6(3;&)?aqFsuB_(V|#s1J{rl~1X+0^0Z~bM1$5o0Z#y zY{49r(ut&aUTg|P7yMVI9yEknWc~$oX^ZGna_Nx>+dqjOHg#Ea7%;A`8OS1;RC>j+ zopb*nnqFlG7>xkX%W4d)Lkvf6|Bm1o>dQ#rDWLqfaMM}TVxh*z#y{EpFH~?f7{Li6 z=M`MM=@bR27#@rxs?AJ8R~fmj#TM9-+B3qTIk)bCP5yz{pm=biEA59&Q{kQ=Nc(VZ zM|fCm_hVsu!+{3U(fl*_cglzwvjKYgz2o{jnD+t`+}3z3W3OZMg$GGNIdO*A0e>F7 zWnd)ob)N?T#V>&M!NNOyjF>q^Xq%=4NgE_}s>ct16pmBAV z&Z6SmL!37K&})Vq&yIvPC3VNjNWc@wM3m}Y7ihNIU_AdwJ{wDyKd2$-i{r84U@m>J zfjo?DPF};aDNZQge6i=0sOq%AOb6h|BXM*+kaFxVs*aPxVrh)lujZAx1;8pHO7Xyy zHtOB%kJG3V*9#pt_vmEvTM*J2Ng=vM1X^{Mi8-^R@}$MhT{d-F*1-&H`Cg9{2O%B1 zbIo%!r6#c{-5A!HVn8So0_WqV^3DRF-*+N1Yo8}< zQ^}!ZzH94|s>RcJ-4^Tl?J#n~+Yk4O5#8*N2lB9iD7?1BwT1=s*JBz|y-||d*wU8i z`D0)Y^4Z$pDD+&c;#;ing*K7;z+*@S_nc_8Qs5{>`ExIP!PqZ(!;2IT3fx3@JWyl( z%I10)jQ3HMe0^O%whYtTb%m#OL<$mNYqWV4)PWtnl^}*=?i@>%jq-8_+w<8_dIn{n z7FIXy`Sw%P^g4uXh(b(B7oKz{uuL8OdhQ7#p%L{QL*gtZ@pDGOA+)x5FtZ?%UBcr` zk5)Rd*AChPNn&C1VYCmQ=`0#v$Z}k!BQ~et@<8x0=jAkcEFDrmF7bev8hhf6;=5&U z{hXk7)(vs6atoD4vZ6ZY=CWXOuHpD*eleO>e=A}cIE4Ni5=+IL!^Y%BN8QO5YPYbYePw zT3SJ#$QA=-0>!3DnACe%I(ksrz&B|=2AZNBw(9S|sqEo~md7-J#1Owz(Zq58-gfP> z_GfDM5eb)z*)-!yQ9mTvf1>SbiDxS))Tlop2Jd7w9-CcViz{oM zb32bg2`}V%0qf{YKRHv(*;z-+U~_B$+*peb83BSFa{7{F*s?cdnkRgT4kCT8%}XHf z@1hY!s?O}}J5mqRKlkM1w_Z+NV+vJ*qH&)Rex9>!@5l51*TIP2z;5N|E3;nt`6sdA zaH{$k3K7=;Gs90m8N6UuonCVKYH@9drqi|6QkhEK3ztJs0H`i7Z4i)ExyYiWj$o-; z;^xc9DTZal>iHa_)DrEsJ+$59f$h)i5i@N$H-ijb*%!~|Ya(a9Qi6V6=ss-if^3$MDgBr8%uB>^06V^X)xzGXRo>W!5K&4s>m zguC|XVyoX?sU&uhWDic9gV+g@PT3ca)^|$z$g~f5+d4JR*G(uCV8+x*s{~U(SOdir zAQdZxk-eV+MYFvV5rn2+LE(s1Ft>-o)t2HRnkEG$>H}V|5U%6itEY}l)SwQBd6#Gf z@6{rU9rV9@&<$G-7Hi$Tw-DXe55>Mi%i?ZTvQ(y=|-`FzQvXQSg&S1LTbr)Dz)XLO#r^6{DeE1_4EvMM)li=kCuAbJ zzSG1kRk`7EN_CfB>0CvIHoo;!!7)i_=lEmLLfSJ(Lw4_o&o%WhHQbCsGPxYg!`$$Xt*UvI^&` z!Gte9JM3vpXM3@x=vcZJ1q`?3Lv7j=If4rZqSDE+!t(NWNkL@Pb@QOkjtkc*U7WpG zC_4zMjtO6!wvOE4i{vBCAD1du8D?HZKS5*#1xQWvH>LZooR!qH&;t+kju_kjU68(DBGQRO)ata~P zuJaiX4pTpl(iP<$>d81%Epv~$c?_eZO+fr6)PVL=yF9`@JMwtDg(^?DM-~XC$oV}< z!{SJXm(w4)zWO$5eEqtA-MYN>f;94h-QA|?eve9s;sDxii&h;uFgq@d(b&-kf2Ts2 zN(-8iVOM&JGAoQ-fC&zi;H}0-{#Vk)+j+#7YCR^NacCU$^A|t&F=+Jzl zg}A;ECY0Z1S`H$pruEs_2^Pp;zkKelJ+2a6(`nd&o{Mhp9?nB8*MO<3^<9oez8F!)Pr3zY}|t= zQIb;#h1DaZ9-*h9B^eZs3^5`ZOM3WdS;w@8aXj^P4!HXXhub@0WbjYfVK|0tFte_2 zrjoWQV7CCvF^5AN-$-*fya#CT*90q5C?G$>a6v*XgCBf+5WX-Q;WV0FG499TyAc7& zsEjC{bAO-~{hX!zJH0|17dpz+Z&Y>}zBG@S%IXVjIwe#p)0X!Y9UVL8eq_tGxS_nr zP+F9eL3}14l50>fh`lrmJz2VNh6&(!95K#Mj-v!MPMmA2*0@Px!(FpX_#_b8 z?5Gmo%wq@3#{Ir2mw1&DA58!V_f54(7CVgSM+;b%{IaXJ6|5%rG927quQ{LxjZ__a zH93qd`SsJg2_GaV@Dwc&>qE&Uiv-PU0E!O8;d=#3ANxyIx-scR1lY=<4J4z3(8@2+ z)6T|YSt6pCqw`oanU(d$LoRQ^kpXFDyweGR5^e@j!<2I4s!S1a=7@)lEFnE7)dUGl zOpF(sPDD}_oY81DL_39^6^;&Z+Qjnq3}-Zv%CQH5Gxs1h9(VQ)XrUoX6;LG7dsx?Q z*R|2bWS*uZa(ICYP&eLN<>w~pLeFV5NgsCNI2j+X##cDuVq2h>Xw zHt~`Uio#yLIjdl3zQlYr=)>!FD`L0YihdN7#aLopymw4Ipmr8P`WI;8oh?)I{!vcS zll`g9zscy)?fcU>4TA@mUoKNU6zn9*Oy|`wymVW88MO9N8-K%`e^NLsQy0)#04S`l zh*X@!>^V%XKXl|#rSwg4afL8B*e9;;rUqfqY0USyj+d%{S{Ozsj1pq7nC4GV7DmFr ze!|mb+08$CHUhf#ZPB5&uPLCXFl&lXj%|Apm`{gR$)L`pKYijbJQ&~t59ceK;7GW? z{y3A1YRL2E_RvJ>4nZN)x34wMF-yL4PY&MJYSJ_dAxjt3YT9|4CjP>X9Gr?WL?KkG z4$Y8WoVP2!*eB{AA|v3-n^{VC3*~mLphR+x)~XgQVd&gEB;KME+s#aAah%jjJr=?0 z_@94)9ph~1BNFc9H-UjPIz;D2!O9KQH00008041#oUHy*N{Qn#P0M&E=02}}S0BvP+Z*F3BX>4RKV_|YtXT>THq8 zYA9ZB(%DL0d>wyv_CmZVH@hlZu4?hW|0BNs>g%sB;Gh2#S4Ayzcv)@ADy_4!I6FH( zKfm5>q?pS^R%G~0iA7n7Sz0Yiu}+I@Ayq9FSuRx!jnB>&Rk;>Pve?$!N+yZO)&Q** zX;G9l0aa&b`q`|^b2;N@`q4bC(^;M>CGDeSUQW&T(mt)E`Bv{XS+NwU64yNVCaqU_ zHZ>#v1bF-ekIpo1v3YSTD}|Hfr}0{%abVnY{Va<(u!We)!|?Ou&a4Gunx~uVJl-t_I(# ztV;>`81(5v*$dkTM<7ng6ZuCIRlVIbaoczu1D;Lro{{_2P8AG_6e|TK37RH1p2+fC%>fCc%IGbv8tZj1RSYGD%fq08l8u0Om^zPtSE$K=ni zKl}je1R#2Z+4(h01^9d?dRt(Q8=3F=Vk&{1g{&&r2w8#iZ|sMGfH&9)TZ(n26msY} zLj-!~at%9^b^`(+PK(9Wf(R#|`4%A-*u9d#a`WvBR^TqHS2S3j*0||3Jk7?UTri-4 z0Qf1a!b;{Fu~lNa6Tq|uZIVJmPi^;AU2oLz)vF~y-%jIMxqd}EVYa3vDKKz!0ZbJo$UbMuw;x`=`R?*Mx%~L% zHEi||m%u)iL{e@*F7~SEZ{uFv|Jx)ovrJH?C96~=cR*y|oq12W8BWVGw^RIBSJ_4& z|K##wwu0%*YFUXX{C^{9!&OqT{OEo9H2%xv)2BY~)tBn!K&aAeFtb$wGqufY!(R)S zG0+1MiV>l8y3A(1J~3Ol#U8Ms4GgJfDF^`QhbsttU8gs)AcbHi!dk#1TLW^NSh3np zdl8UR^(}oWxh`*IqG+!WwUkdGp9g;R1lmIp;F{kNQGg~|jLJm{i2ai0TWP2NEpAAD z4_MC10`_06U{9c0xW%SC*@nV~wr<-%Bi=4owMI%2A^h&!ssfP}O1MRu=TjgvLWA}M z;;yW26xbTNH%YZ=34$xFr1+sM7hXAp#Rj5@yZl_wXBl!s+@Ih5&M%wWF zfc$F-gr}9U@)VF%HR4K*NC<%%&StUQ&GdyBEI}ogBs~Qxc3`j>igNmQFi2qgBBsP@ z_{AwHY#tljN2Z^HYw%;Rx2V5BF%86>O3WVwmB(>#H7nerRJ=uUfU!hF3z@_EVt301 z-^*$#i3xf>M`%^rF}uaKn2{!h6nAuW0mjQMY~DFdm+YN&x=})c@ysxA2P2XQbLHe{0a6{{ z^HUFgd}tf*U(Ds*Uen3I($9X@IN9Vz?z#bh#_8$>3gF3orXaDg!}sY1JpeXo zHD*s>E}ZOufHnBb&IEwj?_m>-HZ;)Rp3jJ=3P&gNq3MO9zCtrGGoAs!0aptas}HUR z>dp0TvpdkHAYm=GgzO z^+epVJ@|wL7()QygL$-I@afF}X;TFHB+hENRz1%p!Vgfff!}SPND95}oLWpqsEz}a zZu^MlZBuekOKWzecS&73N2~`A_Oa>!tPVBQp4J$=EO@e3OES_rxSq!Z_-IZf)=EGC zKazu;=Hu>x%%<1}G+P#BCFj~{YyhNfk86Dl%FCBG;F&C;f1o&!)nRT8j{|KhV^9&o ziVZ4DxR@F5rMN0qvdXlbqqGwZ63r4I)(tDyA@d4DS<2G9ioX4)42xs29b-wKFk^UJE>z9JMa=#Fu`0h z>d;ozS)S8`2^#RGPn%#8Q3xrX1h(}a=ez8|9*I}Tm- z*Ww!>p6%ewd%g4tY5^3$mC)`pC3j!D~0oBL?1Xu7~nxnF3?4eg#Wy{296711ZAfPfo7;g8W8V-Y6xr}|X0rw&2fQ#(Anhl<5 zAk`-In5Q@iTDS0*d1g7d121Bi0n40#aT&7#&Ob#vh+SR7pt5R$GG?xg3u3$sHga0j z#Mz|B1HL{wK_V|t(itN+=t6V^%bM)~?;B$;Xbsu4FEoqjMh$h^QkQN!#UNW-<8?9= zAz}4U2-9rX@eQhIZUNt8V~AQW&#k1Ahd>;5qBS}#PIzHeH7a1zacwEq&>X82xCPXQ zTStFe(t7gFY%XZ0rFZIzp0cW+wX!F!Zs!X7t8A8|J;K3mUChy5-8O@8P$}dQ{IvO` z4bAby3-K-^i)w&6B8*Szjj9cIJub)o8J$kaYw2!P5(-P(+lG-mMm@yVt&WzL-I}@n zn1G6PsJ#cK!azi;RQb9PSsY{7=MRtPaTLSk)+tDFcoP4;%!-~WLA=d-u1CM$-(x&? zBv7=P(oP1b&)&d7vHkUwuX=|4r0Ey4PI@f-%npGLu*nvBteMYNS0H-p)#q8>VM(MU*l@<9V z))~RjVbjPk(V}FL&R~^yBXWWX1lYHXi_Y&s_vq`de)nGyW3g3rVrOyylX*GY+Pu?| ziKX+kJlJFf_T&Z^=`b9y+B2NZ!Dcz`HLf%tC?m5WnoZfRlY_?PQV>P4%7A9dUE$PAYND)vNJK-?dNn^0o z;H}ATs_kJ8hFw?Y*|d!dzL zp2B>VCo}bqj!nZ@KhTo(8=@FzK`c-*Gr-H9wCI3lk4EI3WJ5w6#*8X@>an;d4y5gVxFn z%OF*k>~?ui%6Af46F;tUZ7WH1HrGH$vE-*JIJyF6)a=IApJd?r>^xw^78s=~%;G!N zRG{YMCT`+-+h@BZZgRI#E0nfKHfg0~r=|6;G2jr!b_0te1FBNa)zmq46rVVe=2Cgc z+JWqxOE5<2N&}1l15YqXDrpt3p+Pp?*76b)24Vk=F%(XM9@6_WMq)Bw$0$JZGG)#4 zjY3H}ht8U4Dl$#!YXY2MIs3>c~66sn`ha!&SFEEsoM!(pBJbC7gaw=4#L0c#f zngdSso;$Jqq{WlII>o^s8@d>uu`*_rg@1phWsp31asx0B*Hc*~EGLZ!e~!xnn&J(m z&M-Oa)QC=BxiqdTFd-WW__bJ?j1rNLJq`d7xIPwS2C^0H05U(0dO|)hpj;gAg!h(m zNd2JSfll~$-xWOI`XC__8#wHB>K_W7cD~Wf7CdVu52$C$1lH_^FPk0AX#4tkl)qUUB}UFL`d@R?p5ry{+Xjm|+pRx6#9WJ&N zBb`B1dWar-+mZsbyI)Y0r?Y+g;VP{PsXFX5s+dnO$8Rk)cU= z7*dBu*yA^LCEfrMRR;8Zr|xP&H7ChBomC}Tu-BBt&T7hIV?m0XNfaksG^2p6y%on$ z40gYu?iQ>E(L>EOY`dU`#G4guhOnVrJ1$JZMB!-cW71X8*!zqpNcbTIR`XgPndYi$l0Re`yvG zrCwe!jDM#nAdMeFU`c#JUoZ>0dLG}iIOaO7Zl1nvP0wEoe>jA{1e53_-2VfcAbb9s zx#W^ll~3jo-S5}JAqL%N+b}@q3k8~eBQH#r_atMci_r{#Ul*tSf5@`;Sy4N#H1>W` zdlw2cj^~9wTrO{AK5U+0-&9#yW%X`|1>@{gVbnO8EtY%h|4(Q0gF&l(c~bU5iu#wy zWa4Kv@tGfoqr>U56!d;{0w|0^IHNy@hmBM%fp@je_@P4Z67LVdjYWMZJ=9%w2x0rPzz zpD-|KM(D~Envcc8k=T$D==cy3FGWM!?y;k;(XdgoeAvT^u?G^l(m3p)Ix@jckY;4| z@9511yaDXSlQuF$#W4%4^E zini<+#~ujVWa3lXZ6Xe;KpVGMEun-rlbFMGdzzVnO)*AvapAUOdn@`);d_h2@cl7W z_`Na3IfM`dA7Ju}V0EuEO-C#VFh_FrAiCsf zJB88W*Cfs34mR8ywVW=^qg7@vLVSo(mAeIAocB?WO5kSQQ8J>{hxGZIY z;TxeF+@j(-HcSJX?$_p~-peir6Y54U5%%ON!`tLv0hek$|0R&A*3q+kbm%XvV91Ja zI1ZgWbtn#vYq^LH{!?SiI*P6#SQqfgRncsOTR_@Vcq$k_TJGCp!O77l(T8&(do4w8 zk)OWG+5mK>v(<#6+hvjOxF7?|9N~!u zXOA7Ce|E7c(YEJ1fk3HB+an=R{n#XCkO6}G6trDma(#FB%IfSS*?>s&5B={elQ63q zgZZMV(TnsKZEjhpYre@#(J38m3V@`7872DFFud&x2%?f%RhsP1K09dI%%EtC;tB*M zmCOWCCqdf!11lCJt-+7)PqJ6Is8zYm=Xg34#Y#;pf!6`dMH%$-ku$4hYG!GyqyT-HwE7(R7Eb-55AD zONeA={;r>ay&s6)NlI8@ejbWq*uOg82+D4cMfG*8}|wFBxzO z>A(H?>iyfUgHIm*HW_I?`wmOFiMfh)Lt6{5i?SU!wK^l@<9&&h*m>`+#BvqaR;~I4 zYEJSti@`hUV{V46R9Ok*y$%G3;N1`=Ik&;#poo>i{K?mGS>-%wXpH(ozA7sBs__h*|tFmVh;`QU`Gu zbgL@ew9FK|?KvPFkrHHI4yI;QY=gGJ(N208>KDVlP-wLGB3h#E*4|`vxQ9BAhZp}b zLAya;EP<4>S77c1#PsvPVPyD$Y4P~#fgnFdAv^LRpKf#4i|Pgc%I7_hQc%gzz83n1Ou3@5zg)Pe2&*+ zWV-a7CYG5?O0YXTdDU{R;h|mv{6-!eFj9M;MH)gEgRs75#=srZNqqC^jy9OQFApak z3_OfAWQu)hFlp9hMOF^pTr!g)+RI|C&m!tQ*hMhVC$x}{w5cD={2%*W5rbCOL+j^= zYSzyJ?~-c1s!J@i`>ykKEhX5g{)o;_1@2RRfiCeiL7)`x8=B4oemK|TUD09;)Ca@a zCnbH_N9{-6p?f~xPnV8$S7?4tT4|cjmy!-ryZbLrODl1`U{`@jLL2&|w2a3HA*fCk zISphO`}jg&+o6vy1eko{5EeSd@zY>T_f`6|?2LQfkoI1{k+w-rB0^S-8dvXmHAPM7 zFy)b0>e9wKEp*@d*t^5Ra5?Vb&w{6~6bqo8oRnyN&}SVPad~nIbYEc>2BZ#>@bpaS zz9DSH>e=Jy5Z2z2bV%#jPx|a#C6-^Z8(@DXsP2c z%khT(yBqy;fOM16 zsstwGngnf$DFY1ee->{`irIl%Vn{a#c+7(*>sbtvdX{eK7YF2yR)FHvQ|W-hO3*^$ zmG`Nrv$bp;1bhf2Xhi5yodAXE;14HazfAC^BlvQMInYk?cpO-kY%51rT6(Sr7_~M| zqnRVL@b6UYv!EC`j`5>Cj3^!#!OMUezRex^va=PQg9^d%^HyVKuCC_%#&&3jAic3% zQ9M5Az{w8dm(Sy)$n%|`p%%HO*ueZaNN?SbQ-$e;?7qj^ZH+W^JpwwzR)L=ve7(no+Q?b9 z$YvmfK@nyp-%N^#I^&C^HCiZC`+_ps7wonyhi(F;n1JTsUY{!*3W-ryPJ2yv_8NWz zBG~4dqusL(J|1Cmqwn``?|RmDg1x9h{Vj5RF6FMbulb1zQXlSHybCx@Y9+}Tz! zUGR4p>G7gc(bH{%=6ZMCx^z5p7I{l=41lAKm>v26kDbC+mXR>?KN7!dx)6N8u`$o5 zI!xVG`^jA8%zymFtA=0E`r)f5`vJJX$PebGp6~~Y@V1_RZ1F^3n+)C8_L!>_bn)jw zzO`+;?eKl{Z``y*7ln`fA7nF94yW033xOY$=a?&OKZ zG7$=<2DDMJ>^0{?&ckq9jbZQwp*CqH`&pLY6X zfAkmwN6qzi_Wg!!&5aHonO|HAJ;q+(D-KNHW#0NN0PmUrDr&(bsxtv!94#&SJ>EWF zE{p7+(*EWL)ufPlwi8rOq+yZ&W^^6{PIJ7ea>v2t&I@~ck-al* zGRW?(ib(lC+EntmrjnCZsljG`1FwLd+Ui@oT^CNQMrLf5?bOw-%bas&R6!<$E!gkym-mD>0)|HPg-oIZN>!YnV;C{OaN~8&AsFrOgkszB z$kcqd%AIP^zfDz$_PBe0tD}LjzC4`oN=LvQudQDTZtcF~4Lq$y^mE~m7wt)UhopW} zH7=hcf|||XT~;!3bq2tWYr1oT@ES8tlMZw`?{V%fAu^aBL)Kh_%nai3|JnZmP)h>@ z6aWAK2mmFm3SH%}UdPD>002A}000*N003=eb8l{9b!lv5FJobHXKycZVRtTYd97IM zZre5#{;#JX+8;>=%V~oR7z`9c+N3}LZ!KD{+YkgPEgjojXi_7oB*w4@+H35|b`C{J z)Fn<*G(RGl=R)#azoX~u(}&aF)49k%SxC=wP)@7_m>D}Mc(#G`c=|GV&fe-`XT*Br z*nj`B;W!tf6*d!iV6)9e^p-$F)~ zWNyP96P9JV5RkLVh8tkFwWi7?q@h`wv8C1$H~zr0YrY0NZ?oN@XFUj%gNgt7xBu@6 zif`dy)>!_+I&(Q`$v0rgYca=S`m4gFi_ZNfj09szUb>Ao7A5ZySWM@_x{JOXJO?|CU*YDzV@FD9#|8dz15 z9sz5q9jCw@-xdf@875j#Pn~f}Un+i)W za94m4K8j`4GztThcb-jY1=uiXzcxR-Yp|A|LIDb!Otu>-=Phwq(c3!M^|669hMrHr zE@3U)Fj)*o4~^)!8LSoD4H>m}7G8Sb{vvduzC<0KE8qk(Mgk|IL}~O;@bvusU0$0G z>t5~mfO8_7ev@{iwO!a(2#hH+vd6r|}rVk7^BjjrBs3vZ|(Ef95q z8*y=;W$5Tp!&A}?O^3B~oZu62lz+r?vRn%_vfiSm0d0er2<(V6+cqEidEA%~szQp$ z5n5NbFt6KIDo~O>JvN$B(VFUoIE3Pz34 z#2c%r>8vT>0##U;GKp%iV3%NH=Aork?9=1ai^Y@H9{-}lzvHC9+N^nj4loK8<%dUn z1q^x(n}r02KWXjl!*b*u)@MDQ7BgPo!c5HfT~7Q$Loe}l&0zNKlM7(|{|(|3uf^Kc zz#;$0QsqV!Lq-R}GzV=oaT3~%B=dE$5`=gP?6+XWS=sJ3ZclFM2RjcTBIecF+KU>N z4zfc8C389t7>6y}1QV3|uJ%zg4C3QjZ}g}FnBH}rvnXTLBf)^33KfuozzPbO{cHG> z%oQ7C*FLvu!i4`_S67t};{XSrz3iqMqJeClRozatf*4cNZyC9eKc;>D7Nl{;Zzo-c5Z-O&r zEM!$9DG^Gs>CjFH7jtk}*SaeuPel%gLVFXUH{I#m<6Q4ie(50%R&6>_4#4VgTmN)) zbgi-M7d939GPl*T=y$+$oF6I%3^N-P)TQG=GecrUu^)_K4M`MW%=c zE8BKd0t+)3;p6~LdBGXUm-9M>jTfialGIYJzbk3px-I4iRIn*<5#46#bCi_Bd=i@bk7s6d`6{)dBf3`%r*NlM+aP+1BTZRwqK~>18vQR zv^G}fbeT}mcY~Fwro#Nb)H^EJpmX2(yb+?Ha{7c7dO8dEPdGB~aqLvxY}M8;)dYZh zLOa+*+Y9oHW5MP4KX1R0xj>>oU;u!PU+^@>|Ag&ww6U`Mz5arf`>Z$FV7fjiTgUoX zcA3|&se7qv#lWm)i_i3FBJgaDGoqKwhy>#q(|Tyh*A84`hyDOZ5-G+U9uRJ+H-wa* zi|}exQpA4rslf|a90a9^bP%OY@8`rndG5Z?NiyGfvlpB+@xm$a`q|b>#z$)>9{nBj zwVt85cJ1-9vejb+{iFjv9}j~=UhAvg{Q5A|)b?4^@#*^dexm?%2SU;>;Pgv%R%$IM zAQGqP%jkll!G=M@`4_G+VEfoLcdPn#X9&pbJWTMaGs`)X><5dG)V8@J`gQ-g8)(1}2A%EE)=SU@q=FBqKE* zmGL`ekMt zF143kW&1E>3~;e#U|0g}7xbgKb<<39ETFQB;1ezNn}Ya1adk`jrd^)F-*7i|AiTL<+{8F0|({ zIzF9pH3QL;{3^Dv zNa*6Bzcs#v83Q>!3F$UQ2kK*>sqg44-bj>9_nTM|COc56IRTH{wjVjM;t$O-4%v8H z>&dv3aD&%@zu*e>mQv68=g)M-?!Mthj8FqHEkT9IF57;OjJks^4~d7DOo{n-%nP-6 zCq$G^bpus}0v0T)gZx|`Iepyk2euL0BmJ|MpQkgn>CeeCC`VY9B)stW#c>CdD~k3v z8~WmvyqL2DKAEzB(!HZ`1gVxu;i;=Tm5Y)-fprY8zV?E9>IWVVwzQE-w!D!~DCt6XG;XlJv4zXGg+|vqXV{$Wx(7EL^AM*^&p4w*abciH zWxav_yWo!k!7xo98UO&1`2T~8cQ&?nFt@S(J-kDh8`c|a&s(~EB=GUbns)JS>V%La z8)ra6)}|3GWM~<7#v1un&GDt-jz}d7-!tFC-fP(orO?D1;^9{Ed7;YTTrZq2Ya1&) zUly8dlPC#BGW}=!p{q93V7A4JDd?nn zy3Cr%as>TRBPco81NCAUz!m2S&m>`yG9gvYcmq@sG0Pe)xi$cMCHb$O(wbgV3KH=uRH5BnPm{g_VoBz%n4=)0Cg92GbKuBfA4d z2n0U%eNsrc}i=(Ma%KT&CN8E7*7hsI79%NL|w= zzJ1UIHbv_1-7a?5u&FL0J~>$)U*1@w8sS94iYoe6Gc1H5U}e8kir|r0jt+i;73wPr(f;> zp|K&rYjZT;N(%@Sj|?PKX<5t}Blbca+>a31Hle_L`qdx>h)hSRRkq}L*vutu1K$~C z+$`>aNPV*c0!bT_KnxvSq%_&^L#V~mDO=)! z0Qyp_=qz;e8q8yUa3qgORM9yH|4+__4G3M?Or*YdL+%vGiaI%+7Rryk2uWQIGQpew z)E(yTtcngJkz{vMBURx{%x@fA+2}Nobz~E;V%%=xWIh5*^yA#id;`V+#ufWR2I#Cs z0CjFLswaB|Znw28=C?<$OQJh0XV_|9LnEqn7I-+>b{H?yYfAEK8x~f-bC`N<*oQtY z_=^IldQPYWX;|B)MnlBbPl1(t?-oz%)-2KYRe%o`x@Vt73tIfyo~`FyNVbt@ehI9| zB{GmM6O!u*@m*TeiLl#v3u(4YK!B>)U7lwayvzSM{|KInK;3xuh>)8~%$m9Zg<3;E zH?amq04N!>N6*T28f2r}>He}W?fNv+6Jn#|dku}KhfjdKho1c->bd%}(9@0modJIn zl5jD1OS|*|==pj;$99^4mmg>`wH^MLWtRC=ucaqP3tAwN8vCq*gd?o>Kw+S-9TlxT z%*DDpOmuhFST5-A@=RCDq=~toItW~e@-O7mF_#3r`SC>bn(jUx|bRr>1D3QIby!03BTL8cvR?JV_ zcVLYR5S4$CPQ`i!1q%$u62T{zC^8ustY~^TU_OAGT=u;679eo%&KC#ZJeF-a0m`2c z#%iofBi}>?G*pdr7wLPHKghTJ1bzo1k}uo$Ff+35wiLpeAv-*MQ^g76)3S{{M3*I0;9k{0i$;H`6ebgah+<%@w1 zYSzX#{nppTr;lpb7RgZzV_noI+-4|G>C|WTh|ws3tH77xOXi|A!k-VtL3I8 z#$O}>;*?OEZE&7mm1Lz*paExc)z7_l`jK!2|#GAH1ZKyA&BNaNOb}- zFVb)P#Cr*_0{HbctJO+yw)a}7sOF%E>+`O zA2{gZ06OmJKq#90kvVzmUmHOV^QYWVtu~zZ51N2Kz@Ti%oSxWgw1(jJ+@Nff71+<} zK5e&D9zDmP)2fs0CeC9{5ara0ngjPzWkv-$RS4NKwb?j-I~XL>QG~k&Q_I%C>58dm z*D*t!;m3hWkvTP72gs(btaJ6{;KTg~Ep2}~7-;im@>L`TY0@HcM2`10N2La36N+Ea z%)gl!(02riUmpv`I{8CNM?E?8*WgojVBsKpvgi`sBdTSd*lG>+=0Tm0X58Xu7jNGw zS|^u*MR;pr(^4(*SaPvLe=)KY=glCAb%_Y_d)~k#;m6sb6c`& zbDA^zApHWs3RnE=)4X3QoRReDdn^a&O!bqlJG#_;8j>lP=foCl@~~P5Er}cRY!e!# zcdJLSY(HS!cLUL(FZqJ{vbV3ePuM^fQYmub)W$){hRuxvx~kRLUpI+Nk-L^E7IuvG zr*~`f8(Y>Wrt)4o2<_tcTDLfOX2rV5Pe^TFoBw59=ya6H=IUq3Q2NU4zZW=cI4svj z&rDoD9A;{?^6a7C0l+Z%egBy-+@ON15ScT-zZTBZvw?*L|I4M>5{NE14C>3zJkozs zTpo9BzqdHH3%O!e$-07iBpo!_G(3Sh5^d)&5}p+h3Z93(vOAGvECYxY!y{nK;v0R*e?R{}SmB zRN^Gi);v>eZN)yK%uO$d#)DK7gRxQ*I(5v@EXArUi8NvNj1FhAitch5mAc?VFml*N z(daC!ZAjl-ETbbezY$ka+URiL!2Fd`NR`RY7@;b)f(N?d8jPnEj5~Dn6!YZ?q{J+Z zs)r27hYTwMi?euD{gn%Xvu~t}sOgDLi`P5lE0TFm=5;R35Bz2-2t5G>t$LKfLW4Oc zxgJ~NJ%zgSzTrRGj`gg`SSJmc+RAd)`z!)CIX~wQsy|$bFkV}`Cs4FrEH{C%1Lr!W z?d*T(s=Obs5VrWQy8o=3w&WvTDVXcmRP{BsQPGvLqr=5WTH=EhLA_&zxr9*MCOb1i z-z=Mn+HGunY>kNSh~C_=K7Mz3Le^~lomV}eHLz9y1ezrU9IUc@+{RO@iY{_&Qz(@4v5HKu?ard zXcZx76{JNoFV=YCZ!e#ifs{RMFl{|N1=6%+6T1ELA1Lf#vM?DtKmdS$zkT(8hEG}P zTNwY};Zt7m^cDkj2th7C;Q=t}m$4=md4l4cAbRwAzdMNV$e`bszeww1T?XrQ%;Nz{Cm5F2!Ya% zkf9vCi)>9l+pa?dY1mnf8DRP<;oJS@J|X-19FYICKD_|B32kL70GduC zBgFdhLEydHEQCu4ML^+hC`FClda%3&uGNM6WlYnXUQvHUTpyg#Dc)E zG8uuMgYbYb+_l#q!mRx8*A^v2?faUZg{t(Kd@{A!$oDh?#GTF9pd4=DqT^-$MkK?! zlr6LInq3P)9$MDC7z0J-SSv%c>d=&U@q)f&qxm$?&OfbPy5Llut2zFV=>G0 z&}Q*yvJxYs(5hHA!cO*ow(E$X+Q5m|axvz~)l=t)Wc2LDHYLmQkCQju!eq`R0*J?K zyS9zCxpE1UO;p7bg}qUj{`fJCXnWY9FFRfjjWR2nG>fMbGujLMZM7V6X&*dew|b+F zetUt(y^ovfKM=h6DEUW~?sIIJV&ijhX6TP83jIARxx(ho!Q(=^D@((Aq%gv&gH%yB zvyHRzg!P?bD5++#AH?QKf_o(O9z@NEjr$Pdd{8%+>hVnti+&v*%yd^z#?OipaWoe| zh5pG8)&5cdIy49W$sfI46pL14`?^E5}BoWr_b0SJEl-Jlq_Z1M&N&cVp~V)-7ZmAmAk*0OHxZ z#Z7WMbh}jFk0O4)i}(9SZ}f-L|eth#12?j7-#2c)b`86*hAmz zJrD_a!Nfv$ABpL3_3lkqPxoIfsm+#wh=q$7YaiFH5L?f;?{?N&q-XGgg_wZu4Njb> z;Ux7&qgmFFnP7CUm-l=7eVr^BZ|0N0^}7-J>6Ai1dI;=jQWi9EUJIt)G@;g{qTRJO zlO>QsKiIk6q2B;w-BD~`ncsn62BXW30`C}hzW@-7uRf%fC}6Ytct6vFKn{yToK?4O)xE*}o5%pXZ`R{j)EjbnFMZpVINYtfGgA(q6P8=-F-v zvjk;=s^N>jqc(^E5COp`ltZLRAr688;<4NlLF3huSGh2KbcjYry#pT>d9W@ZB(@xE z?3e^IxT|56XWZ;dLnh|P2mkM~2cY?vpa%n!^U0NGB(#^agoRpZI>W_!R~~FzFH!-S zm1zTjz{A~Jf+|YEh-0NXgtsk<06ab~D9YDHs@HRLU5x@i#q$6}z(U2tv^C-*e1jB& ze-+~i^eMN!;;y^B5S4hj`pEg8==Tzc_gVpKA_ux|xLc6%Q5Oe(`EAeFiqE?4{;RV{ zWrXJ_1_pBxZetWyC&IlPV^h?^f2mn@e_=%troID9$P&rt?fzku>~mPA2SZuqpZyNWhg z07dtRF6hXp%^Q^cpmX=*EX^}3a(zj+rqOBr#MGFx=%*N5eP53c9M~9H`L&>9#ev+~ z!w@Ua@&$(z?b&oA7`+cmHpO_uaupFuG`tg4xwNZX>MBSNQ#*V9I*myu51GGo+_ai) zBkt3`ac+BK*Axtq$#T~t$k>S zcKr+=h}Li7+R3dJwMCgz;=b*xz#EA3#Cmd=17>!$Z>!@Zh3k>_1*Qx%U{$@WasnUvTQkZhl~;+0pSEQR zIp0>Y%9RRZX^UzzR}@PaZM$hCETgIeVmuU)lqvNZ_3lY%W}QcNpp<1{XM!(AiMhF- ze=Z-D*AMe4L#?ob@9$J02z6;dLcRmzlH<}DI@gRJ8-~JLf3Q4A7~c5EoL<4H;aw?% ze@Lr*w8L$+kch#Ffn##dEyV#>O?4@1}_V$@R< zw_?cT9hC}Fdx@1!%8IwW3QpucPJoBMS?}8)V`m$4y=7)px1!$j+Laa_lBIey>25_MOocPn%iZ(x$i&+mOGRa=MM0xabJjI%YxXqD2oB@_N%;5w zsTWLuli-M7KSaH45Y>@sl_lswukfToIwDq97JyUyAUX_Wc1k-Kl@S~_R;s_O$|WiO zN`qx3TC#M9L;S#wN4`c#Al&&|K&{s{z)^tDDRmh=T^Sh$u9@D@2%|(Lemv_MN*Oo% z56^(0%#+F>wsshW(&X>@@fj8K+_=1*-45EU*uYf!OUM+^rzJXx+4?;D!Rjl(Wz5k= zlpqKD@K9yJHvKE^1n1QBX}~S_>SiAvA6u_U%agRVEFt!_R*o6 z#OQZ@9piYB!})<0+l3o&7kZFn^E%Pz`Sn`=mpMIpgb^iZmQ>$Y3PE@m%Q*@^qO-!Y z>R^^@-17;G#+qW!!hE;s#yLH-s-G0goi}aO+C@9gP)d*qNNElZkBZH%mXlM>h@>~a z%~3y)O4}`K%eI-j48$d;w7I=#mI3shWpGbToIJEF7sQ)n$NrMz)OqnOI5UtH{%nMy zr|{U0C`8_yc(wE;Et%Ba>oblA{+ncYd_;L&`i%75*s&i2C`gm5zE(ZEOBvHAg2hx< zX}HMGI8LlW)PZxBkk!IHTHkUPTKKJ#oug<1h&!0-cPrb@ z{|H>ofBCpjoo5u;&5rGdV?jQ}1C>vQCP zk&~z|k+PUO^xy>PY{2T3Rea5@jN|x021VQxE6XepQTtF{O+8k~TS}vU@YLih-0ky6 zV>#=tKn(sIR-_EosyUSmO7}?X!sV|&a4QI>&Rld%ga{pYrdZ&RLSyJ+n|dF z75DT~b0%Kh2SZk)=`QwOelJmu`Sh(@C?~0W^Zxgaj54F6%xCwi=-I<)z0RM9^}i#q%5u^GegNWw~ZO$@DQ-+prlmy6>~$S)O`b zSAz9p#ID0#xwPVW4N9Hn(3sPi;`{}#kZLG0t6U;6?3n$t8?^~cBtfZ)LT3QDEFp@D zv1yK)Tse-6x(K=bOWtzBF7~4QxWZZhZUWiPR7m@tz+A-iGJqoHrjP}1-j31r-w2;L zJ7>gEa6r8h*1#&L$%M3Taefk;fIwYDR7q3iW=e z$+Oks?fzag#_TS)uGgm~xhF?RiSsisfCB!$MEUsggLgFLg{h*7^`s2o+&#(U<*&wV zS#2Rvh1a-LU<`2qz_CS4;za;3%#v9Me1(HwE`h;6xL8mbLqz^z!Ib465U)@b*YHp$}t1+0psf5u* ztZdkh3Q?~1f1|%Wy4CGzvVrNu2`-T!J4HFm*MMg#F|T4;7A+SqP22j99%=i>F1sf4 z`kmn8^*%lv@rk<>AH(i+cBiNBPy@&wl>vPc_m%Sg>HG%>l(;u2~?)vD!k zA8FdoAcX`D`_-M?H0nQ!_3YGvw{*QkmCYzICotQmkx8-YixrJ16@ZRH^gvC!OaK`9 zL2=oyq2`aS34#U(7bz!UfdW0km!Iu(iw9&7v{=6woKgm%d2CV%z$V;*2({VbAS(6{ z%cZi6(0Pdoz{2MKb^bMh8~X7l1Eh(Q^i+6m72e=PaHVZC^}BMjaT%kmr8^ag)eS3+=K9JY97t19gLQp`&=Uh*ftgu0>F0NfvbxLI)G zJcrOS-K#Bg4%$|USY=q_5PvtQDUvw!N- z%kGOMpul(6gO8Q)z3@rnhIxoAU*9w`))Ad+6!%k~-f3ChNm%5Q{C1NY*3Jim6-r)) z-1X3D8y44CSTl*3DT+)G4~WX&&g3ceaWuWlZp}!yRo170YlIbU)~Dqi0&U}5*BNBK z-^75?NY=V^dQE{x?l=T=-U00M(kYvixwnV6h@uuGp(=w3sd9$CZuQy|L&19m=atx1 z?$BYnFqx(H(ea4hTK8Y-FUW_N@+_%crTD(@o>@O!2@~WW8&FcAlL z`zK^AyOUH+*RZqYlo#aBaj!Xqy4k;f~KNj_7wj?gW=9Lw@ z2i(+ov6lJ8oF8!CUp`I+d{$BcKcQx5>s{ZdphhhTR<5xWI30#fd^oAQ%a_`rbRSW; zjvu^I0|b2qGv~+!*jD*Cq(VZbD8%lDw22jf-!}j|a;f9`9OO?>XEX}fmeyO(&cyUj zDVbf7xO|O3S$13{OVH_EvK^GdWW>9XpJ9 z_q3_g$A0yVl@<;x5?D{_w|vHN#AzT^nF@0-W7Q?O#e^%YoK|%A?WNok>{KHrXv~s( zn*yyd#Oqi@fn+?SKq5L+24O&m*N5-n6EREv!2`nL=J4U`pY2 zkhomw`Pw~ttHj$5hE2+lom=+pDDxiT{~8G#>d7@<12T9Au?H1*$77S; zHG?R(dr{*Yh_YJaSyM=U>^r{)Rd>YA?2^XvgEfgJx#HBbJ{~8!_bv|Sz34VnCqM_L zzm#^?!YU`%Oe}gBOIYxQXW^Jl*s$(+UoP6OKZ8C8K)m)DFIrNr1%5uBVjolCtba7o z$!W{>xdF?-S`vu@@Ci+ZT!`!hG~xthOC3ad2>5nzJKDPUwA#F`W%m+k`sBgbnyF~iMS2Vlobk*;o=utLjdT(QETurubs$T10wfUK}UiSYYDCaV)YehEKu>^=&^0y&LD4b-a zf=MX0mOu=avJUU8#GI+`JZ_q7kQe}w?_L9dgtdK#?m;z$k0V1cE8g}oGy0P=p>+DP zz$JNDP98QZWVpN`U_v=e>0Ce#9(+nNrFyQ}ci}{lVzNuv`R9{XS+oKrVeDzDBqC;S z#?+_BWW@4X7%O!(gT4kuxP2&uIL7)SUNryZ-f%2}*}Dzd81&LDVvI<_O__Yz4wV#_ zHkt&+yH1QxgdxER<2^|%0aJ5dVL;9QG_I`-Xb?9V{EF^&k>`cq6@P%ZCNTIJ0@^)fe zY4obJEqR6ec1s{Ah4Eg0gyjK7?hDL_a6Z`sEkU_p6_@WV*yPqY6`Z8nKPbArV03_o zCeXed^rO1f-kwEH1vdS@2`Ki39aCUe$2xo}Z>)Z+MnGl3UFX~Z*UUzeW+bQ81oE}@ zL>%uP!RD;FB%n`LhteV8E?T!2A}`p>j8fSu-W@*aEhS~`c5^s74trDp)V$`G=-Ci1 zWKm8pg4NCbA09DWm>!O9ax4hxeLxk=im~_B%0axGx9t$@$$E<3e9(eOr5a|0f+j)hTdECmht%uT z*4jyT)P4WIdbud6ywJUmk~cYzPjs&bykb&500Lm7W0EBNFoeq%@*J)qPR$J zx?q?)p@5k~`Pi^jl6^L$f@}D%4j@j``ttM@4zZGGoP$CVUbR8Sf%yUj8bktyWM%w6 zJk#8uy}-_pm zKFEN3zyo#om2dtA<(tbEcyCDA)poJW>WY4^2*GcjL;O!>z@rqNB;QnHIJGYZYn5u- zi^7U6IfkDY`P6ODVZQS|`4Z`V7SFaFDSWg$IUIN{Z1h!kkfbbwwTl5DNFi|`4^i_Z zXx+?!HT?uNhXK4-RoU}oYXh#|K7gh_cAt+Qa7Hj!cQ5z-m}Kvub{H2iYh$?flw{Z9pnm{iLljWL6TZR5=#d zcd_0b3NcJQgUE)W$_@aSO#e+PC@6A0^iq}PF-g2s`&FY#Hxqq~eQW}h+=($BUkKS(}SutRd_8AxnNNDv629j}<5}~C+W?@W6b2)kBy=ADight&6*MEq< zt!qJ22FsP^2JAXOTE(Dhk@ZlMAh)z2_D9Yo?3*C02&aSCFQkm?MKGUv6oge2*q3Br za_w&-Epm7u+l@E)6DlJ#LPciiI;?qa{ze73^qddGVhv^Zr*v`ttJDh~R=du02HC#3 z-j0Zk1N-*1+^V(#S2%fvk)`g)7aM3wv}zaFZM-Yx2Nk+xo2 zYlIP0*i()An;3NpJyZQDZezY=pcv7j$VvCgsUrg^XdgB2(MrVXFfb65l=^phBOrnb zJ`dg#Jp(wFsNa(r7S{t~R)%p#T^xZ(@uw;SulxuUMBXlx1ijCK_N!gcHfbCo}9Sj0OXu1Mt2Xyd2Y_w~Zdv5M>f6vbqX$V%8h=sSdZ29;$DvaatrDqJjo<83mdPs^$YH zJsx_5*ziAIU-G0B;vJPnia}{(XXrHyg-0l4OYazBrocvKqY?BW6&T!1dFS(*$fYx5+Ewa)!TJv zP?=dNo7@R!82DoW=n{@;G$p(F!l1yGH`hF_GgZj+Gv_-3&fYfg- zvpe}2+%}Ngdq*pxv;c1XB&#>lr;+7`c1gA7-7^y#Hfax^X%5j#lM`mtH-kTF3&1n2 zZSFq8#BViqWFXf21afyZ;}NyqQ-j=9{pITcfUxgYug>JNX#`~bb(<_p%x`3JyLMFc zv1jKi!h+VeF>rCEN5;@G%$66mlxBp!a^?}-=bL-*MA{R9qUBzqb{}~YRBD9+QR~p{ zBK%-ci?tXoYmQ;}I?26?I@Wbd6^t7g$>%ayN+&$vWX?7KxkrK9VY1-1^Mz;N;Qh+a zIw~!kbIYCWa_W1E2?Y0aFYLBVgQ60nqF;^&0IG4{@un5QHSV9Vf?EIdd^Z+ma~lo7ZnkK8XmJIGoUOaI8HT+-5jAI4I@ax)QtKP(>58iC}aLi z3Rnj&)b5y)NC`A+o#GhTKEGW)*07TDYGt=6!)3n^qKzBP0S|ayemKe>%ekl$z|Apj z&T~aF0dPIrhrbsxucX`DZK;m{L$zP{9Bel>MQ=YY)tXLP-qxQ>DxT%q_8p=@JSG6U0$^H$PN`klXY%@@d9 zx`&h&2RP$|qM(~t;H*DtYq9Gxghx=G!o;9qRbyS)=perko&mbdqfTr5s@v`Odbi5Ddu)JEbC8 zCEVq#!>}4(W43WsdWsye87*yWfwF4;_;it)2+DaQjv2#iZqbH26pGNtnVrc-VmN)J zSC5Cha)`YVQAk_)vu|*xcKv2?W%txU?SjqT!^v_?zs@h;lGY?#x6Wo^Mz8(qs z(UA|nnKsE5|18vLh>pw~`>`uT5I`1v+uZ@&SWp^8{ul-FBUE7`T5}Bgg=rCH_UCrR za=fRA)^N;D$avLm6`kW5<0|@aK4!9U6(_GTqFIFsJ7iiux=#D{pDk+T#>B_9z=#qHx;&!ptwo>SMZi_uuXmcY{>r z8oC35?aNptc6X0DjE1p4TuPcOo~d!#fDvy5H8=Za%kD3J(BtJ2;t34eH@mLykIg*? zB$r{v%J?W?rAFCI={|jTvR(}Z$N3ZxKJOeZc!MY+S2Va*`4yd*TX5G9_OY{2uZ<$o zx0rlEjE3rRAPRl()p1#ezSV38|N5*^82bp5vCj?6B0epj+q%hm^P^729O6-0I;a`N z+eUOt-*%qTz-ebdBe6+PL*A(K_8P(CT@~$)lrhBG$^6w+ehRMCFSY7C1zQY0ntDrg zexX}jOwiQ&7-!IyNKZ_{1ztB`7MIJ^w`W>Wa%#uIf3*lGYo24RW##?TAI`FpH}YPMOn8agUr@rp)f`V#i+-Z6N+#%&Zic%_?e3}3Z)-acYEz8_polb)iVvDmMgoJ3RvB- zQ)L$~p_h5~tI^p$u+w^#60NH!)_D49CPxbR8gczbGwy}1*<5mi#G&a9=W!3se-V3- zdo5{L@)zo{g3U(avgwndipC7DkON`eEZfaJ zm&N^}M6SMAmR0hZ9L-QT#9jyjV>2*Dt!vc{pi0kucG#T-ERkAO7f7mb$yQIePPTYP z(2YJnZ$FHAp)h3EjA@OuL>@wkspoYT^u}m-+fAb zEU6JHP(vqJ{87IqO@8$OYZoT+6DmjEICr3VH0zt<m`~+GnN%ft4dmaYV z(A%Sc0S_U9K~ktP2Q(n;Gslym?_Z(piuMBwGS>oMDJNU6UZf8*wTJ^ko^5>y4RIl^GOv$R7`6CIS8q&&B355$6FcvjgI;uod#jr5LN;Bm z({>{_4B}g-X7y)5Ql{v}+i{auc<=%&F(VCw|I2sC%q&5wm%IrWi0a5rI!~ksN9}OL z%88|I-R2C$jYM_99MqGeEt>*S-GBgR-4gILR;qYp$c2tS%qkIxCC0#+L`}os+E_CG zO0tt}lUkZ>6a49ol{T|`I8uy%6)TEWso$lwZ;@JH+YG79ijDpA`E>5VbW3N)q6_$T zB1{3*+}PSy9Ks?p6;M#S~&`=6M=8 zQ(Du6!6B}jR}J@bcVsMbHoNCHX14ijr!Ai6*~_)*4d3_O&?xrFR~kYVQk-z9{;KIw zILS9FGr7KMg&x@^uCo%)SBVZg(mip{;o(n|DcmVBb2zYoi^w~NK0b>LygVN1ImPo9 z+$M|bwUjMjw9@Aw%k@@k#&u9A=8;kNo}YEr1NVtH+a)J8(!!F3*jRNCdal9{s2^1s ztK65%i1|L4r{ji~*uPhXOXs^CJ};QNh8H%BXs)k~8#rAs_-Sx{cs}!xGnV*?b_ciB z@tX~c0xPHWT7B{aW*R%K9m4_tp1CrQ#eb$+LPJ+pxv-*1dY(VBy?l2Pyhma|%%HM0 z7Imo%yYCCuEXmcyXsJyv=Kp|n*UD4fbv6iKtDzZ^A!S%nLmtWChrdTZj*6n%a@$pa z?-t){w#_1)7GH)9CC%m-I9OxhIz&Jh4n{3Lwog%Zw#d7+(j=-k6tznC-Ig;FI@5|z z)^6;d>swR_0_EA{;Am$*26g}%0$2mH2Y zgLq1}%=M6BL)(LBQj`Da8M1xr+{DY0kCC^JOmGDIUZT!xqsk;Axwgn@qL<;q#hOQu({i384m3> zN@3g)_WO_CWBKn`TQViF&%+FlIxM0UA(Cj)n5=GxTe>~=SEnq~!nUmo>uqr`XP;me z&)?ZsqY=SquwKcfM&g>KfRKYyl3$e(G@6womEvJm#|H2mNdR|n8}^ZJcBcZrFH7Rl zXZLL~m|-GE9&O+0V8}e!^yMFG%9wsi^Q>Q)gQ|%)5RYOYq&v`0k;rF7e!yu%OJWJU z$9lAip}bzL(DufGakvk?CtC-VTh<5oJApaEZX3p3L(&+)LI`4dZT~-qxVXn|FtLAt zfcUO}fC&G$7u?d$#nj2c$@IVa-yrw;-?#hbSp44Y2W{N_4k%X%(I%@SiYYs;W&&kb zJUO4#jLE<#k^Riyj!{vXLq#XRGbq$?~NDSZXWaj@Z6} zW$a=0y-I5;UvKVY+#41~e+NT0ZYBZZr+R{edV<0tf5deG&5}ts#Zp_vl!bOfRpA7X zVlJc&{@ehU;DnB5nz5 zs^sVw&!ATe!3m~`redi~b&LQtxutz4VHCi0Rc#oJEb7iiUp?5y{@Oa&g*}56LPNaJ z@wQfvEa-wxplCaqmat^c1#R*04@4Z-^?8arfxS8m`|gps$n3%gZ!GH4q~HYb%3~A~ zWe-+j>Gl1Z>QUONjw;mobG8CJF&kgsf?C<=%>WHlQ6)Qrjo*#TjPU|T=O}v`-G+z& zY&=a*SXOhi4m5d*iUlz(dMCw7IRhD~?IVi2zurFLv(E|i(HcfQ5CIwgX5QnEg0jTL3Oet;JBlHU7m!Bqrcc!WJSY8-eMYwVSse$_RP1- z>=VK=DRM=$UAO2;|J$6|W7%2=*gul7?x?TkJ}nG4zrcnJPeit0y1FV;INe2Y;9?YuynxI{c&JSlFk%hrI$!)k5z#O40cT{1vdhw~qgrKH)t2L#G&d8rLn`#Tf=B~p=A19<; zyez}$n_a||nEpmPdLQ$>66wS*jT=bB^lTA14}M^SaFh=21C1&c33B=y#ibrOuMy;5D5{F zO4g_eM?%hW~7F&YZ>I>2rK2*?!Mj0teaTdaL_gRP=W9SD6n%gFxR1>4_OSc({4@Pw zyXpyA$o_SrI0`-o~%vf?V5r~$=KscFk=YiGgQ|Cx!L{GPlQ z^5*q%@A3VCeaRD&q6Vkwtx5DG?|9pPx+7Ic(!1gCk0#1d|4(}&;dFoc{a7d>&kVaY zuOExRuSXJZgiZ0EPA&4a^!Dd;zW?~cE?S-;c4qgM5R30{lgQ?w5{cRAe(^2JUQXAV z=*xqPpFsZy1!TFKXGh@e+{4=un=s4g4=y2L_ULWw>ndIDb*^FeU{ZhT7jO#KFHBK~ zl~709e(z;R#ri6^O#zX1>C@z!BO^rXwhQu=dl&86IR&` zTBWO3@*_W6bpqg@i**9@pO1Y%RUUlpUG6iCh*&aj2@#CZZw*PuKQBCnv zuM`2Q#GXAt*E`-$iRdSRUIv`7OQ}uf@HXriWJqaQkaeA!)AW%tzwigr9G%RQWb5X0 zf^V#Nr^oL<Y)oxAn8iGVhVVZ+b+$$_#aqi`q>jcpDpSU6!N@PT{}PJk;qx~$q3+O@&hN8U53 z%&8+*S5fX0g37L`Zo{s&Of$c3OQh+f3Ae!;oH$=>Cd~*usyhs+#)aU658?!OJGD?HwW&7AZVC7mU0L&LR5CaMzv5x$wG2D@FU^vI;5)dIh(BSA+}$ zqav$|p5elsR{9y$^U#Su88m?p_K2i6RcS@KdBYkzg{puGG=Yz#FuGLQ{RiM@p=G(i zFFe)*Aq!Y0@EI50WQWjnL1-Y74c@_}-Nt4(6(1>s4VM1FQ7m@oTR|jj7!USs-ar4o z7-;XzS|?d~%SVx4bzMC#S-}>Bn*$z1#_ik?{xeVyHHS_T@e*9|kK%WZ*>=FRq!jBq zC(!UNlTYMK4>3r9wM>I4$oB@X#&k4dMu}*ZM8p<5*VtG5r3MQdmt=E#*v#jc0e0Ky zgbp!_U}Hrw-aOMYDAR@6MICCRJJKl~mvQ2Y{{)ve9H?`%u?r8)uXpcRRec;Da)=fV zo(U%)PE~GDLh**$lP*UX5ke)onRjcAPeI8qA1=KcqDRbzzrzhzac3W=I}+0?pI&AB z4nBb_zt}lHBdGK-Mx1Vyy{gdom*fc-^oGWz*bF+lYwD?*6(kyZ$)e-gzJcQ;k4j`Z za?e&zq zNmVIC!eYGSU8+gTRIGw42Qed%_IGB~%t~<{miyS3r}^NYk%ZWSH)Xs~Z3;Ku#&!(# zu_hJcF#YI}KLe#F9m}3zVtMm#$dO`Ffu)GUg9c&$M0`Z|9yAj-CC^Csece0ngk&2v zulmC3^M2pk{7!n1FmYX)U}2ZaqQOi2G&eWxQu&81oK|^{Cnsk+g~~LC{KG*}V}dOQ zd5a&)=!3ZYKqP~Fr$=#!JgUE0BBh`Sk5w;HJxx+R6=%*UhQGx@wMTS7xap9a7hH1uZ27HBT2t=T6rsX&HZoq7!w zW%G<1@|)ebTmBH=VK-DW3k`A5DGuk*nyYCRkJG|44N?e17vAU{U5nz)>LQm~L5f(b zs!VcEFYkfjuSv#Lc|0aKPSE42=!}j7_@}LT7ax`xCtO;lIL?$B2Sje*VNp%<`aY8v zEr6I~|Ai-Zz!${^V1hfvyh2~mpTIH% z+-`Vfr6RK}cN{c)MebGAAaS;(l1*yj8fcfdp#JTdvMboh=u z6eILuP`L8x1s;Z=b4d3>jgW_56hz!(c)dbB5w$R zXN_NO)0WnTo>8-pUK3I#O=k(t=3#3U$KCVTwd8pRV}qB7k#O#5d$<^JW*0Aa&Jo4vvW6$mJxue?QG~I5-yy z3P}}iaGT$uf?9M~3v30X=F7!Noj%ho>9qDq&82?+$@sK|NrB9JxajH_j?1i41r>t3TWWzr1X` z=#OZJpX74&Lij`EV8TMnryb~CC07p(Qm+Y*`{R?+|0k_daqtU1VHW+a-zNMLh@MOw z{zgPev?PdJpBZ%OW&dzcxy)t(TvAdwk8`r)8_xwW5@^d;CSLZhZ%u@RL*^alvw!CzJH9r)>26p=g$U+HAtqgCbR7tHWzcKUql z`+HKYb~)P@Ahd=B{>s(e_NG7M3Vm_I)77bAyd<>O| z`b1PqDx!k~`yV4Ku>?OTb)GzBxoDUwqDJtRAc!a`fG!xq>Iaf=Pu~yJ%NYCNd!QaV zl7sHMKb>_{dWA{m>g(apdIN6RvCJavx>Ipx0Obgs`ArPO3v0d5Pv0c#ggVrbcb3eC4c$5HnX}9jE z^SW`MAqc3X91&9s6+MytA0i*8y9ZN0iY>ZwR?j$94{u$jas3&l!)fQZbj(TYcK7ez z#-0dxOm&)-Wb<#d*|q*+8=EfRnC`RreNl6P_t)-&30rdnBsy8PgeAOq>;1h%+JvI3 zzS(@4;j8uh(b9bNtaSIgP>g@Zr}MJ9^q7I1KG-p{4DIs`Vi79Sq7xj}5W&4TFbTF4 z=7K=y7}WFE{0BL;fip^P&;lv$rmp%Bv9&pMi2a7<(J-YB=I07D(iL#cX@-69~ds| zbYWchVeS*Zn@8{?ic2&YqRP+&FqymD|l zCEvkbylnj_d3gg{c?E}doPz01U`E@-S^K?%rOlvu@#(pB)NNBzH!=zdDvH9sxVTrH ze83sPqymy<@%fCw8m%JP3Ys>e&l6}epPC}y15)n4ED~!jdv4mxzmqjJy_aWI+>T24 zEvc!g;$n^r-F51KbWhp~NY!q3Q&l7Fn}QRNG0|8QEUwF~ge5plvLg0wTuBL1PcPkR z7V~)!0~>oS`th)0=fE(D8VYg0g{BAZ-gpqye>e<$`WI>lK>sK_|8+a+WkClI%;WMF};;OBYcbq2L`TH0p^ zs1Uun8mgXvMHKWZvv_GQcA!*I+t`j7e>v9ZpOtOTLT{TU{x~szUv?^;o-2URIyT)o#;Qjz;7~N z4T;#R3&};v92R<86CyaZk2R6t)Ebl|Ia>Kc#h;)}Jn8FW1)Tnlk`Kx-kSZl>K z9jvRD8}#~oUbypvXCla?JibRb$9r<2Hc-3mF^V;U>HZeznt_+?+2(Vhb;VV@yUxr5 z3;0vSLmyWXk@6lRVr~DKQy?Nc+~c=&F%WE*t|KSLc!Qo;5Mj|ZmPN+)LHu^`R6eD1 z`R2$CL_q!0Nz!!Pjb=xBSma5SiA%lm{{48Z-Sv4r;Ym!J8h?sQ()4Sj&8!=M za>PZb#k)_Kc`(gt@s2bjJy#=>g*8QF#xXo$7eI1f}94gQ>xyQ?$B z^ibe;I>`rqglodNe+?Wf^3PV$m|d-w4g4O{`VO(aF)a=rMI>lzEoCD*4d=|NE$BuizTzx-(pF8jw;bX^s&|c{#DNeh2lD7$)Jx&;H-^^*L!VF zdSQSIl?(q11jgm2oyJRlKB5QL;3>E5lW_0ea9BQ?@g_C|`WVAJfPMJBra1oQpN*@k zm<3-RQvt*_6Zu(ov|8LwzzyK2S$;4vpGwemZ~maI`er;oE!ife+C`w)6`ki)?{5YE z_-y1Cg@uF1Q>x8j4U_$_s|to!G+$bOK;LC~S!tkYM`lTfk3calpMw#A;DUhXMfHM2 z_m`RIAH8Jf*PNqh&J)oIkTzZ_thK4-)9z#PED`lXe%!~2*R)up4Ah8GS%@+4Pl5I$ z`LB~@+A#9MYA$OoR+@%`n-lq`xBYmY!)&_}gD`C=1@n1{^dF%|o$$hGoon+^7PLG; z4H<|#wUGBB7dVlP2j!KrTMg9jORg2{P9J{1yqpuB?(= zeoXn&3*H&+IlgtoP1^%Qx-#<})HQhHr^)hMiO=S332+%v4+wgw-Y9G>Y_+Z6;D&hY zNm6wnk9=Vetww}H64LD&bfxj;a<@x-;RfO-nGND?W924g7lGNCBZeDy{-YFZp45x; zTv)&*_J8e6Y9aqyyW8_F$dYt)tK#X36x9{j4LKWF;8j3rfaO>(xZ-%(?iLvwsI; zWQs2hHo!|JTknfKZ6(iVTm3nk>vC!bv$7R*^;B<{cDfEbq=LWH^h=Tl1@w9fB!d9} zlp{Yal0~1SG)}6F86(Xwx|eCWqijSljI*ex-tC~x&K!Wyc4OdnmdTaPz1-&UnuPmb zP&gqF4eJw9z%g5i#B4=B5UQA)3L2PP0Y0G1aZNi zuMCl5mp?(bD8{-z983nOV|rkvcNHLYuxs-3iv`dS(-ih3FnhXGeyS1{v^g!T%KUQxoWCO3>a835AGBUc^j~> zE$y@z(Z4`~&4VRTEwA!KR}mbzjq{qoe8nks=qmN8@Z9Fx6fz1EG#VqW4%whVEe~v< zkwr!rx@S898J(H~(nmR~uCjlHJ+2<*+C}@5z1|oblU-L3ay*u3Gl1jP>HN~vqUEaQ z>O;fXnh=$K`YZ+WCzZ_vb1*vkb-kw?{>7?;?s_^v`!uNM`nW~HmKl#S%i@-P&;h|9 zbI(dvGLnF$r5<61a{NonqK+aQ4PFw@UktOoDP@Z_U`(*%_u+}d_4qv>=l1Rw@=z4S zql)8;S_TXRV(0DR6YMpt$vI=43J*QR*DFJSwJddRjdZ{lqvX|aQ|Vv9U*0KK+nKf# z#FRP0Eli85c6~U9)xBNF$|)4Qk73(1mQHo>x=Cx~xRC7Z>z$C*$A@ z9JF;!Ge;NSpihCwgY0onk-LLWyucvB26v*&!=^*{Gh=V^vD_d>4((gUy5u7JI?O+U zT6qRqa!^}{mDl~);T1nnruTa>$rCVTpAam(D`?Xb%u9c?h3@FI7Rg%@n0k5}g!Tk~ zQ-;q#AACev(&33tt~Q;UfNo5gK*xiL4W;lImrZP^GJ}0p+rVT-uR<#WHF<%GI&dox zk$+;tCs~7orh|F`brQVz>nPt31%St0VTZ$$Ow3!=yVyA{W=Z9@D>Ch^B%P6V^;k42 z=kV$JdMixV(!K=DC_1>!&2B$|ooIwaC`xZzD2!*Hk$B2u1v9DYw`U+9m&fw)D@5Gc zpr3+nL}?rq-}L%rBET5_r~VBNuU6H}wuGrndlh#e(QQ`_I|*JJDHe}`4#=}PMB$3; zNuH}-qC?3rpJc}mgyk$p=aFSrRx`8_a^H{)#kCx4d6Om{v17tq-nY<{aydh5Okl*sMHvN7s^KXU@z{xfUEs4J0{Fd*M(uE?tS}B&vx{b*vB!1ii>L({ z9ZOg809#RkAoswMlI{kPiK4?5mHP9`Cj!WvzLrc*%*Qjqnbd{)2O|5}S zn+l$Rl1o1eDzyuJ5Luq4>iSKIlKxjfC+rMJ_NDD>i~M$>C7j@-3|;Bg-&Bs6R-!3UFNCLwv?4-vQ*07@F%#|QEVBDW@}Y2bd8fN_vjU1!5^Rr9L@y0f1g;LY(RHmj)sg;n*ontCJh>mP z=?KAm>1@!Jg#3Pl0e8svAKjl{Kw|HYT=C1%-AX=BJoV zDd19N5YXUsutlqI7$LvuLsmZ%-pc}3QsXSwf0MOw+%cExyoquhRr9wjJq*$t$?Uu} z<-}WB|JaL|R^t1jafeKpW0%IQLMfgbFN?>0ZL|CHw>h~n5xaF_5fD5 ze6DsX`>73GPKTUSJzpS`I&m$`AO0luey52SXC{b!lf)dLV{o8Wv}Mx8@Fl21Fu*@KJsJ%; z3!WA-5poyCm2KK_ZUR%>Fna%lyfyoiFOaMIo^k48FGL0Qg*Jb#51n^k17F;zrPx401Bgdnf0K zD0bD!81W^kZ|WAaHJ|he_hH2gp7HG7y5r6B=YIVSMZ!SlX5R*cRZwX(H09HaF<)E+juCQkFoXYh70@jOy~EyE{DMp zUC9X=h#Xy0;#t=?$FMk(8VC4b0=)GPr9AQBEbt$>0#V2lhfjg7eLjD#U$-rthy2qJ zrvID56DPCx%a)2mTF9hmaR93NFQtFO2vhgG`Eb_NJ6?)%(ig|05n(QgNxfokJJq}f zD6Z3Ia%fV+P{Gy<0 zjUgz?vIlP`4%F{evfZ=o-P$u&+=;Bvb@bUWq3@gA0$4yt$UVeCTi$vAUqOIW!6hn> zF!j6J)_D~yspr#i=ydlUES5`(8`AG(=SCIotu3nV z#c17d17{pvPVNkcV==`tx^z6-thUALxK27YX;1#t+x(cfMK7!->1+Koy(5!rH1Gi{ z8It%BwGCH6tWzt0nPa@{u<|*b3p61O?64+VP6Fa%Zm>+}6szpCPCm!F1q^nFgT+{J z!Ap;$o?;f1M?i+tG5_vOcd=e(0pb>D+KQv0ouAR+odYP!`$Qz*^cE!XLv^ih<^B6$ zE-{hqs^hc40y4+(hgyU2C8Jy_)48V^v~ z$KIDCOV%n6d~EJE@mr&R@_|*Bj2~o>$H~V}Y=n2W|YQzwT2Z$?TU_;)T8e}0O zt+N{`zV_-GEuH>XI9$S!lSZQ4Uxs=4C|#BieI$wO&zM_Wd}J%F|8S0w(tXDqSnG|P zUx|uQuzEYxOvZ#(MgFYVaJwq8@k;zwwZ-u7rMgdMl2(9c;DrG`=`Y6UCouy-kfGSN z>;jx8ZYVvCq$cjwj9a#`pex(($0B8809Z55-40l7Gfmq1CsCw`bZ3VG+9#$TGbzEy zrH~PS^3Fqya#~!T#gfl~i`S`nX7`#|E@OAsv$4NJZ_#FVPEYdp%NECt;bUa%{hE7# z4$jb;lbG!gB5~|>&v;!pl{r2U1OPHfyuPD7&xB!G<_~cE3??5&-ASQ;$Yeen(xS4e zP*jvDZ2KxF$z0-e2L6=PH#Q*{O3-(T@x%u&20@R)DP#1BHkhTQ#3`&_&cRYGbpb>m zXk47py|?Glb9o)NCVV~ZK8=Lve=4=6LI63HeB#Ep`=>waYwa!ysDEaJbPuq*9?ZBJ z?FBl`4lX_-6BcMDPw#%|#B&tt%Ji=FRNTgqIa~y9Q`w8ZdLDMMhuTaR2TnBYoowg$ zyR`GW{86m}4$NHrkC>i%)c}ImC?nh4|5nM@F;i`wictl2ZYwOa7C$nc2xs1>rZePl z{-{^JD2~NGfca;Ov{9+?>um@=!7e=Co>aH=D0`t~-m3)5iFnO?G|LgF(RC#l(IjH? zP!SKKHSVALH+&aq)-Yj-#Z6S`>s!?79wj zx*|x|4h9|{WSFRsFlcE2L1_+waS+S$Pt?t2L2+h2hU-0cN`+{~1s}yzz6*cD%#1v9d^L~UKe!x*u=;UG8z9g?X5AcuoEr#DU3v{aCRQ$0 zUJE$WMsE3V$M0--AjYgWF9&yd0L0vus1RsI!$BOKmKF7WFbLBf+G0mBOvaU#7lIY( zXE5xucv~Cu-c3^)`Z)o)+Y3K`d)t-(f6VMm>`v@&$Lkrg&r{G_T?2b)fH{m{Rl|*W zXN&gep?m1zW4DBw>TS5@a-f`7wb219(^+(K;_oQrFmeiZcK^aC%AuP*)+>@RD7ZSXIQB zA231!%)tq+4XzueEE64`p_)!ZrI7?&+QgC; zM;!aChOzsZ8K2*}IFQq2A@9@(Oblyws#BZ2e(Bkf5J;~_1K!(|V*g$U4%Z+|TH3?l zgF<#(JL*v^oBi_Yxwzky4O{a4FOwTABPBkYW$xd}WY{gp-x6k;OVBuozd_J6B%{EO zo>7Y?^98rK%KfiwKC{MmxIYsVMcnw{EJ?zP2ge@usA&&oc@Fy#p?;%$Av5uL=osL3 zlLI;9BUj%~ZzVI%NE?j&1bBG(_yqVNXc6kAWk}sd_aZ6?9LGX$KEB>jV%{lwMmixm zZLlf;>oYW<7fzs+9ius;X_Rv|5@ZYjS|83E7o?_Z%fa~Ex^6SPE>9<&@`k^Iy6E_d zGv4fpaFVE%Cmi70VO86NWLKZhwO>20gvH@xhFh}ar_eXYLHWconOgS7|@;cCUvRAMa9wH`jL)&8CvGWMe@yV0UMk( zWUTA4|G&%Cm`xGJxmWm}gy=h-T!aEOTfd5kK>svyeIkiE#?d&g)3?#+yuD7}L(mlZ zm;^cnShF5K-fg($mBaIeE}+`!#lqqF=>BP|?SYL$IhZT18TGa;jik^T#-(7BXtm|I zg*NYn&8)OB{#G=muT}!#>nKZp$4z zmkV<)3Sd?6x*zilJo)sY-UIbA{kO(61KO!~EP^^4yLAPJlt3Jl-%W7YUJALyc7Hom zt3`kli|81^D@u#sU8(KO(i@{O3I(%DmET6Ok2OSa<^M84h&+B89sFc>02OT&--HjS2eWnl6=vhY0)GBZA7&F$(sZztjRl zek2ZaBZZ;W&ds|32MnR~Fq%c$O`Pq4<67)V3!SNE;}Q3D-k8E?+5&j{c$6|#;`H_v zF=oI|C^D!uwAH>#hWWPRaOqNFd`vAZV10?7TUEpc0!(Ng`ZZ)Ou1cd*tM<6rEN8%G zmmlq-HO{m>;ILfKt4dj~97Q~uGFEy`Vj?I|Hava$3fJ1K8kN8GpEV>?M?T>%JkZO@ zkxDB zKfKE$GMpky-e+e8kE*PPvLvUhP$40vBd&8xs$qtw+BH1|svjJdc#r}eOOlt2x=z$N z=@$4(O5%Lpi8Vku#k7P6G9{Ah_Fd?yXfd{&xO!)-fz?iI3QDu{maXA= zXK8|E*K7s*jXF@GsYr;jgK)s+>D@C~2SLlY-@RQ=LFP_}kfn4qj`Vtw>t+^NBFG$x z7j+S`<97SC_jK`5n}9vC%H+;Ivh2X3OY%{5lvc+tu;+$!+(e232!5P<=d`txiLGvDOKnXG&zv34>8K+FelfS|j zBU=Six{vJqdqFq36S2UCc?)pPUSw`B=!CU0%{QaeeH)^h^-H{^S%1CR=Dtc_l-BXt zZ(`MPlWDK)Z9D)KrlyNP?xjf^CLCkGX{U-Y9+3azSROA2dCa8pqy!IHTu1dX+PPn| zVUIP%v(}AFM@xr7X#|@43^A3cKt?yv#D5Gr4}*yYu8ROxK2q%Y2k!dNA?GTbDAR;@ zc5{^4Hwr1GM>j7w!9(N6h&_c`^E^WQ<)H$A{xzW+!&JYu)uqy?BTBX3ASJ zU$k_@`O8h6S-gLO@8#OXHo;3wBxT0Qd!gM+(U~c45loGuSOF<2-+aPTqo6w)bWlL~ zqIHw^B&K=Yh%H)p~ZqH(N@yiV^ zbz(d^p(5E!qxpv~ezc~uNq%ChH3-I(UoR#-=(;Yi3SnI!z0)p4>1k^+cqe5Isi)I(NNXfVHDW!j!DAK{)Y&OY+PEV?J1J zC}OUPKb~z?8w6fah5LXBwUwt6WRpkviXPHJGuc$7;_~Yjl_M$+(SqABs(p-jVJ*k@L}i*4Q~| zJF#1>4NEa$&6{f@fWE;-Aoc`}UrbyWaEDUH>cO>wd?v`ya5?2#Q9R|Mia3j`z7Ci{y;?)N=eKtt;qASIt1>pg#dCKhF#31C2fy*M-)C^kPp5G;gcJMzXy0lae}^`8&IZIYC~n9foB zqdmm*q#>`atL^K9yvkxF-=^PVZW&^gYl`oo@N7{*;gBPsNIFGfag+5bo zbpZ>_7)wLSBQ%}g&@@IlrgUJ7dJ1HavCQl}XS!c={S7E>tirNCj&%7kCLX;Q92%ng z_dG=-=f0I#M`A~{VLYpHs5nF6h!TGM!YHJ=pR!{m%j&bmfDx&Hx&NVyoI`__r4|Dq z?TdrJ*JSV4fDxN*a}SRHz4}-%CH5t%N>klajWo_1M)FEIGmPklOzT)JDbI7vDi(e~ zpt-1q%x49!%2JW4c4WaM%ZZQI_Y6B4+KqcDP@;=| z8?Jep5vtw5UXSviOIjngRB5I!w}^9~$j!bQU8TjsC-0Xim0Y%sJtcv*fkKqN5J{Nr z4pd4tG_*+b$g2UmvqaL--k7z#H`vX;z?5y?3-Z$FrHuS>&cct%ob?2T( zo&9}-I31&3zAgUS5G_bpui$FCE6A)ymE|P)n9XVtk-Xi;&mjmN;m_bdIsQElFYe9S zHPb`7K=!>NdL2+85|_D~c~6^9Unvap>eYEy)h|;wjP9e0 zx1=0^z<;uGy$xv@ekZ0N1(u=J_c8YJvj`(es?CXOb3DqX#IHyh zF+F1Of1CQ}4zoupT3nTG{*-_wtc$~~R?;Vf+) zKf{HT%YW7mDd5{V6Q@JU>xteMP`M^1mPx7zPZr2cln8QZsbgr`iK6_apMx=T)}}Z2 z7GnSmA^V&2z*|Cf{Qiu~-=AZbJoJQlmfO&Cm91x5;1&=?uxD;Vymb4#dE=( zWh(XtSZJJoBso&&lVEEM!3E&(2mjm$vNLocGYlqFu_A>CA1yRrrXI z6R7&r^bbh-r!lA9kMlPUH`*0)$z71%e6k?_^+S~72|B{Z)zf6UFOF(L*1#E$k2Wck ztI-T3NQXfq&Grg+JVAlGhgMhYcUqBQHdG%ipX?WQ)aWh6dC20g+%GsF-Ei{C2Xj2t zff10dant)ug&k<5DwexejPHmB*bbQ%?zqV^Ez~jehXJtT4+Xra%kxgEnBUYG>MP@0 z+^fXWvBo4{hON!xhk!rd3a6_D&1~!pKUm(6#u>4{oh|$EbvXGvI7~PK0Lk>-+I*M4 zCoaGGwq5)*82^?eca0%M)MW59ie}_+l|0A%pu8RVxb=88(Q40VKqKGEsp{m1Y7)T z#rPKv8VGE^%}nPb6B_{e8dSPi_jZRj$>UR1m;?FypJAur130wQ#J!dCPw#uiOy=LS z$`ON3XI`6|DPoEHE!4Q2e9<3gBwOAX!&RO#h7vl)+e_5gVR9~nLRWj|YjT%eU=~RD zE}fIOLO-sC_YX$Tg$tR1Yg=Tjum#}5&gTX)m!NE0p>CMT-}9%#a_m&?bh>-J{E?{%U2aQv&08^4ODQ-GsWWlfQ((oE59o%#*a7P)z7NVj5mrsW!>>fvT8CZ-Gisd3>{&*SRE1|B;Ux5V6r z1uy*I?p0!#S*1o|bEa9WmhA{A)g+u;aqWi(QaQPWS)0|Gni?sm5%lfjN{H;fU(WyO zWvuePhVAyZU!gaqWrCI&O#n+k5LSq)W1=|sV6)wQ<%eQuehO(0w0434S&LXV{c`rW8 zbAEoF<}b-XMNDgdZ~BATJhSe5#Q+ zcr;TJsa2x$4s?3KFyiFIi)tI%z8^YMh9S*w}ppHkAcNzIg; z0>5ATCvGpX=N>{KLkGhw(+Rs}x!MQUCZ4R_IWo1Q`uDbi$~b`xU)82adSp^(wR{+V z6M%L7JL}VqYpk7dYLe(vfduJ97}W@E>ikt@LF-yTlqHm6{Hc-pKr1E{ql84N_%$); z;I{mP>hd06q%wr)Jp4OBs^R-Q8_~YEVQ=rA`Nmwbhnghhekll!UhWayqeDQT-d1%O zO#R)D3!(F_2@IO_;UAKT#)>}-LC!&d(tZ}fszf2nF5a2K+V@QMME65B_)tk1;k6%X zrLw61MieP?^>O)0fosYZzo&H}EMgy8Q~J)}h9O6)OSK!;1sA%E-)405*jphar5{H* z;dko&gYQM)hu@ZG!SQHU@BNnyM`L`*K9&@@8$3`UG{qc-0N3KZ3YtayWH)M;u) z3K_yg3Oe*5ZXs_6?8{B@reMR)maAVkf?-d(fC0r;nuK^|$yZdEd;LGEfRlJ?Z&c3= zElu2WFD$%LF;8dD7mO>r1>{>@d}S7Qa~k^`H<( zspby)E5cz-YY>>?Q5tat-ft4~F*Zq{ku<)GU6T;P2bAPv9>ECWRU<<5kZJV}RfOZPnuVo7L0f zlJ1mQ>Y;pLr1k69ffIMrm{cjr<@7eL;XI8(6l`H;bJKx0iKf2nX`}V)iJK=Et}ITJ zt5vp5vYaR(6~=Fjo<}T`0GExdfaB^t*P8|c#X#B~kMtQ%} zx@KhswsWqR&)fYFiU4h@ZLOX1JiX`irRyt;CM=$d*;qM$V8q)IMEBI{E@je*P7H=GwH^hnQbuH@dD2~Eb`nL z3b%28P&+8dn&U2?NNais6X(WCtNq-Swz- z_35sgsy~mZ{~Gxy$-{r7dCr!shSzYFLOIK zLpoYqtM6f;m^J?K^ySah6(nUh`KsNv3VzvxkerJ4qrH-ZS9`10;PY_qB5$qlRnM z-^I4+6Ul!L&n3y4L_!Livd{B1>{7l!0;UOUQBo{|zzsUJE)!MQb6`l8oCOBbI?F9D zu!4q0+yz!UGIsb}?*8_+d7ayujP{HZC9RUh*C#|itHFn7HmAl52!ifmw$2e{#zrLm zX3i{Z6_(&1L|DiP%Osu<+(VZ-_Qp@P7v4Oc7_e!VB5D?IG(y@m*UxVTP z=73F~eXzeoe=D{>XoDP&}6jo z!Wef`rP8FHH@5h?=>>m0ab?Y!Ic?Mm(QSNP9MJLj1Ks)$n^oX=f9dRcbd2U@B*lOqkJiOL0hW5@D-$?3@ycoXy>f?+_k!F=69CE zmFlxP3PO)#(y?9fPGykHetL@CbLZ+J@=o}o(;y)Vhcx>!ynz5Jzbb~{n6USumP`~) z6u26W9J=~5)+YCz!dF$J-mjrSY#HDtrWNPV^4H{Awltsj&zY`v?yRkYWe9~NFU4)P zs1zo@Z2b+^JWYDTZpgVz3b(N?^Sz_pxr3+w>+mg+eV^l4&mr+lK9nL4>m@}gqup|| zitYS*nF{dqT-z2BZ!mQC2{v^{7-ADmTCFR1kIsAglpS>UGNRzN=Kx;HHfP#=;uyyyuaf(1R|B^zycWPYlqRR9R*?6;rD^QrE-bdLazT)V`XO4r153>q~uAE2W-sb0yPgGt>V#ecMkCq+( zKWij~Tj|*g?eK}@P{~dQQ)Ju<_p{LDvv^xq+mFUQy?HW`Hl z{d|@iJza$BrG*<&T~5!6Q0c~=!6PDD(=_au3nz5zm2QNLdwr=PdF-{^*w!YL*7N8EV2RNZv)Up7ALAl-B-WhxF$VJBh8w`Fj#&}z)D z2C3tqf5>FyZ`WWo3Hd_UfGPXXZQ^p?2?4xz_G$y{q4nyX<&{Jk`2IdDGdsdEpmIGDkZvIYRl`|q)U3D9$RVLuEiu0;D?*(mxAxc zgA~SJ74MhPXMrXNsiwJT#zhl@iV7cu^yh?Zp>O#L-Pf!R$=bqG+UdsiEqMl`VkzLMh$R zKEA?&FhJtFKy+wQz{v@L3GKJGRnA&4B#86lQE*_49#ScPO;#1neywKp@#!5d(Rvfk zL0B`=I5Oel-2W-ku_>eK(F|L2Cw(7;N7SP>H}g%S+&XTNR_YW3@5}*QhX7rR^JzGb z(BcTF7D`20?cpiIFsb257;ib6lU_-kriD>kg0SfacuNUWr#cVo%sA+|qR5nJLPoBG zfy9R{=Uu5Ge;v2owKCweNZhD&j9*mmcy?yOk|{}gP%aSJzcH~+3mNsN@w#=38Sk{v zdR9>Od0gfCfdB8SpYU0bS};BUfMMYOg0XRSG%zx;H*j?N55{Jh=UZ)4JhAst?J3?% z?q!Ibz^m13GGq#op6Lp4FMx@O<8T?OG6uzXoNC-Z?AWij;=;DP*P54cyyvicd`l;7^e(z8h;xNbLz>p>WC}d8K~I5pz9uha4%+A0iayQ7H>A%#!9JSMjtJ8@92%k zom0?Ip2i+12LvWq=1aIHN|pH4Sl7N0bnbVq!)NOftR2UhbcIo($+e~n3XTlE*^gTN z1vL=W=Zh;dWEZ~_c`^wN7LEkFJ5NAgAOS)B$5vO49xWN98l+JFR?&6L6j&BKa|P5M zfF{6@YFDUPFu;qoaYLyp_jaOqRM&SJ{0$SYNx`hLc^MGM zNBfxt0w?c-{?cQcwsW6v@FX;x1Ndzfwfj*dq>3r&$~dZbS4`?*KF1~@N>_@wU38$~}>uB8EG<2)IZM|W=UEj|9 zT-nCE*$m)OT*o;bX0Fg=vEhR4=Y);mgk{(mdAL1j^3qVD*8ILvU_N&6eLNkJf;HODI23Ji8V|#P@ zVlc(F&J5pSdfI3PPJeqlGdlJtX!MQm5i}86*L;X+7fU{q&Xh<5&b)wEvv7iq{7KAEemU*T;_uSjV%`InpTPgPwGgd#p^V2lUD7Ep2!f=rryO0%l->Noil5Fky zm{ki+4#>Yt@2k=s#MPSGnhEn%l1pGKNEN#2!V6kDD)nIK12w-TqRVPEO zQbynI53WBBrjLNAW0TQB6Uvty|C?arW=R-)8%3F)vS}1TBaO^aOKOE}e}kl&#)dnu zkj;8^XZSFzD__oY93KeI9;(#elSfO|+6=ul32;`PH3tRM)oJJbD+}Kw zG_^i>_$%klE)IdgT6M|dd1TDcg{q{NsbOvjBLF`h*GCv#~ z@|a3Of{X$IEIFh^>M4+MhPnuu+4rH7lKLMn8KlHRFdDq56|g>uWrq4dj;OP9va7s@(-AtWDAgc##<>ZqWLQ<0mLLeo$?K3ED2YbxP!eZ8P(~a%hun+m|A8c z=4Rfls0bWf$tTYEm)AwO6D&HANuy_)lW2HEh7iohf8XjCG(2 z54NB1+tYlYHY5GEhW*BHNAT8`C?dLV*1$qIbBY0V(H@vI`WDNI4_dq#g)nCg-6pkj zDU|GOrNyR=UGZD}Lzd8RB(@0F$%E99#9%d)PIZ;ZNm`5`u-qg$R(pK}ln;(0VuoYV z@-TeBo%t7tRz_N)!How~;4;Mg&37zrG%`v4To#cJx*7J*#@cF;xON(W#%NEf&Iu0z zJhEX$SqOBYJ<$4%f!zd60eevHhMF_|+)i5aOqDn{`t@ny!OQ*L=jV`c?tf6A<=Eaw z7DtoOa%xP2juC=kG~oEVZrPiZL=$6LekK;BGTvfv_Rnvc>i5Q8+lm#m{X2wpBCHJn zwRY@ThP+=O%o`H$6#t#-9#aaQ{jQVHZUvKA9|VEWvzF2G4_Am zSsmx=^t8H5yQmALyu8Lrvsnrm#FY`0A<=tdvi%VSe+w8>|Y!ctEYKbWhn@&llJqOw@O zy6AIIq4x^+V!h=g%S(m8?GuayYaQZ4%w&s)sw@Zs7bErQ`&!Z ze2%hhuj+sw^=^^f?aIBwg3sZszmrmGcA-mI-)wp8^0}rl*^t&mbIc0kBo;944G2INp1l^v*zlcDnx4u!GJkO9Gy)FVv~tjT+6DjxrWv4lA0CW z_l8h}G4zc*u3W{02IWbx$C%b=qM>e~Vu4^`fxu?rKRW=!xG$v$S=o^XDO?t3SZ0T` zH&tW8cV_}Af#$=U5!O-fwnao*8~uAiZP`RGmY`p9lgB|Lf8#sN1D+!ST`j27`2@R> z0^qJm0Md$L9(~C+A>0Me@E{=~Ni+u@wZv^morajUTw`CB3d|`&QuJ-2e>2LfW`~qN zKSq=cU8IhYj&%7fp#L1xj$Yy?O45>u@!ax-tA8#@(OZy_{4jJQ;1t&H)r!E4H+%vx z6=_R&@3Eyr4fv_)(kpcst%E(xv*|-)+I%?#sS(01c(abc>A)dQ#khLP6UxMVJ$0q8 z9l`p=VShqk*7LH*eP*_trjQqG54Z2r?3A4=9=_8Xe{%*a$=`>`y;p!EOhWC_sO}Je z(yUu+0xt6}9v$7Rd2J7HhZQ_ydZXN$j>l|0a}ol`p#{~24jv5{J)wPiU@)#;KLjA5 ztLs8&c>Biu>Q`6PIB-49^V&7()^a}ge*n%f4U@PEBYl^PG=}N{nd_yd*pa&Qr?eF? zP59$klvbEl%MpPVjm@peXM(dwNTO2S-gr!$Tgp&DX|b@jNrj@pfTHT0C2hw$@>8-=#e*$v6VD9-xtm1X0Vu?}IyEFHZ*lK*m z<`1r&+oVQ@@>_=t2%?=xm9W#72&zW@8-6GUoo1_M+6Qz3?nj-g(O+{BZJYC8 zTODZP8qTYZ6GiI^pj#@!oLCvgO-~5#t3Z<20MbUdC|H;!`idyi5OnhOeo5h|v(Cs8 zf1)`(#0DGCUz_kA{z@i~w6^SF7q(W}bXpVVOxF(ERcC6!c_ zZ2>T4P$*eXP)**kV^j8z02xTs*~S->Fk$SoPXzgm8r)8qV{Bzi*X6#7bhoa_itD~) zjIA(zR*H79K4zzJI6eR~3#x^Vn>{rry>xkOS$tdmNR2|0fg*u%+VC^t;rgH{3?o4e zAt5g!1jcroqQ*>=5jN4jP|9qPU>jlUsGV~P>L^!zZv%Z?EeW0TKvf2F`TUQU#gN(^ z+p{ZX6b)k&fyl2`KkiMX zqxQsV=EQ7k16VYOyiMKe10$lZP`u9m&l{dLgb-2)Ns>ro3v%bl;{_1sJd0);^~R7gn>4FCJZY50&k`BE;Z1!!B421Jw|pCx;?FL82i*FD{ZxwmD%-9UL5PWm=O9;nJLlt8CYJi$XF3r3QI zX3(s32D}`h;uiot5VID?5qq;HDjjEt$=E-D8{0=fs>32zJ#cc)!Ug~)-(vGDan73# zLEGe0!5Ok>)`C(46AHXUL8Vg|#c6Xrf1rUEs3HQsS->h+p}>K)lFpS!Z0i)!k0crt zboC)H!6?ZTvUc}~nfd3?N`iu{nnAH(0m(c_HA1o=@{-j_=|xY}hm|k3ifuN!54UMp z%PTb6pD$1ttl8*jhfCFva%Oak#XB$lHnHjf&TLDt$gt=NfioocMAat@VeJ`2c7P4x z0y>b~fK({fYzE3%t=}whMA)g+Q#9X_YE0Q_0Zz|sOo9>0qzV9^lWZFqeK||nH&z*gjcY3&sR?*c&a;)2F=E0(H3ux~U&0oUAtRKORq$%= zVQNnfxOz!&L`JGZt2CHLQ;RHwAoLtS(}!l07ow}qZQ|S(?9Kn>K&Me+7P_te+XUMW zht(v~e11i-xny51O>pI^hy9S^h;4u?B0THsi}*D?McFsdy) zy@SsnIh~PaH8F}(LFI$hGiQDBF$7EFVdL(K9*W5<^hAiM6A%tyBmF?W>VIFARVbbj zKv_nmCqjp;5+jUIKz0*}#Mu||SWX@Em>)_IrWgU55IYLkaV1=YJ)Fr-2C!{QB-_Lh z5rn+0!E4oR%bMN?+ZHe$9?5Mshkq}XpscMTIKBZ8X`XK~TBvRoYTCImf1fL!CKHU1 ztS|1KhXL3q)FaYc?d;y19>C1hl!w`|8N;uMh*OFh1U4g|Z7?rrCk#fTy)<89_`pR- zFr%u@#65VXpv+hiq{$GqyrVLuwB2kOuWNwhwK+(9E%DB^?4ZPtWu0Z7qe?^2Ih3B< z`S~JbZ}S;0ekTllj^P;<`X@t*hy|)h^o3V}I%1&Z6oQmF66^cPv&}GjnuXO97DqEf z#ZfP}?8KearMN{1ZwCJ!TJ)2{knsV?(Y9sIB)m!{;hg~>&Tx$*Ix#ulRTY#9-}wRz z98We@?z6kQ17df%x-w;@LL``F=0Yi)dnHg~prS~)wSk)FQf5GzJXbYnUF@{3&Y(aMsfNKCGY%8c~QKI<{qoMh~VrDq7|5{zL;`Ov4?L6S7Ag%LEKteAdN#YIkWrF3)`rBu8xOg0rh-?GPsW>w7LZ3SYkLUO)T6ax&sZr^p^U<8Sn8B{gK zl{TmE-?{4M;AYflWeZ=D*L;fKU#hY;TIupy;Z!4U`=s z!?d$FL3N&EW0|6<OrWaeUru921<1Km3kx8}8OO`ni> z5q51ZL9e;8vT?7xH!u5{L{&q07j1(<+d;l%&!5L{$hDymuW+&ky*RnPiiIz+ACBSQ zz)fo@fZvz*pLa)Z=&dq$PPb3rvcCXwp|nkh?kil-0yW<3>;>D!q$n@GJpI2o zxLJTJnz|C!>di9OO4V(t%nSQ9$O3Tb@pgb1DnNqTY#oylqK>%}TX#*>Zt~5) zz2?gL;%9PG5Yo;kkSQ9YNi51Lw0=fTZJ0_LDEFOnV#~U1z!~YTOwct1g)EYIGQv?b)E6-rg=Q2dpUClUzXOtr0 zy>&q&EUYn$(M|0D#YMw|b;N2_KqnKKVg0mP8qiINf8l|jP>##^oYejV!H3$$ROaGc z1Y`x0d_97%m20Zh;@zv(kXkyK7uY!`80*tY#Hu9Vs^B4bpJn6Vg^-!;--KoNJ`Zfi zl_LN6!5h9kA<_DymSg>W??S&P3*_YbaffOt05FNL(;JV`l`*=NYMqg)4)!<=3oMq= zRX#Ewaz{w%f-DMMp3?1GD62EX3vvv;lAErjV}qcvGDuXEnR^Y&*AgT z$k^De<7c(f_op{%_Jje0SwA&%<;_!F55I9j83->T((+YD(ujdcZ{DMQZA0 zRnKqRaDWzW_M%h+Hn@U$pKHYp7*>gg%9si>YzO(_JT{DSu(T*&sS@%S>yu)!K-maN}{!l^3m&RNxv$bYSpESkJ($yfiIdBYnV`(4S-tkwXJ)_?Fu>Zt!q-R64sPZvHO);M;rhgjPzN9v?y#5>mQ@ zKs-m%4ZKh3#5SO(6Gni|UX?Oyxut!5hmzJm%th+$prkk4t_}qkJC=5WJ6mmMa1bgP z{p;`Bjb_&iMini(ERCgA1%!nSbrdb@vesmg3egYZ=a@CDCUq<tiU0#fh&=@#mvO%=b{8d*kAZ>hy;y(78B0|=LxN;d&^Da>`~V)3{#wn{vOf_^lX zcY-A?ldwmEHmSS!d+uWvtv%v}UY5yR-*QB0tUavdr!cvTf?~yAUjj2?6*OXz4DEA& zn1H2Fg~X4o6mIpA5ztd|ma`5PWcjqbOaU@#56blbFxX-!O<^^EhcF#~7vlKvs57w1 zy{2dPd>13@#!1(*G8yE33p9S(3(H*vW+_VgC_mtiCNM6nUo5yyoRE6Jm;@ZJm<#gR zGet~?P)K-p{o_pM>3BlXUN)T<0?D%-E9cgSb-`@Hd?P~ffKdnbx>s(=;V9Uc)?eYR zZ$bK~mjS82L^<-qdHnfBglT7?QCbuFou_E461-&a`9q6KSNLfSlUZ312lTKhZDHXy zl6V-va7NS~$;}a0u=n$(J}RJRU>(5Rs1mEnd^b-(PDF(g31;JV2RlrhqoT;TG6h$O zR+_@Ob98BM!j?ZMPfSSn$m9Ds4wahRco9(%!3#s5nw*hAg@UJA8VKviW<(h-HofZy{)C?3k?)>H{kJu499#JL^X${fT6oi~Z@ zRgl@LJE=gCQrKTwxPvI&$fgNI*QqksQYA-*MEL{{)moAYyKt;wp0^{v1BthFuyS1)EE~#kx+7$ zzL6-|OLj7!to;%`Lu47Wm6b}kk!+Z+qb_&49hIZ2B}?TRhg}t_qWe2`NGxhSe0U;% zB6a#fMR#D>cR8odN@$W)LWU>lXum$D>bE&ab>Q<8_??99GSjuH=jDaw~XV=&WW9R3=^vRQXicc+JQSNNI=gj4EpS_n!V zgyhP?`|g~x;bgEKRC-^J4^428#*HR=a-%8WaKW=qKe$g@(xFn7)p>7N_NT%H0YYsw zob+LSwh7#SRG%NQO&z-?+@?5`y=2l%JMmwyOm=8mJu5e{u)i{mAEeN}T2idt)TK~- zGh3{=zGJs;n0+7VXE!6e@KH4;!aJjTr=YyJ20l1^>znYwv${0beZ=07Q9gW_%Iw;t zs6rf7ZgX+~F=&tB(F%enYs5Y)LBHr^ZjSKq?_iDQio@Pc=UT3u*htElI@h|jJ(jnk z-P~Z~=3wMJK5S0rhOaVao7dN26&5j5VPIGD>+Dh@6&&Cir=xzyUcV(1jQIT4d!zp5 zS-BkR)b3R3ufPZ^_nLz#L#7G!Yas{SYT7y2>@>Hc zdqq74f;Ty0VI@x|@BvTEG`dy!&=)ky zQm=0#Zyda+6|S2k%$ilpIN`Ue=7jpyF%llM3h}`HE?Sd^q?GRaalk75T~AvdiT^kT)5-t-42Q)|ustr)V=CZSC|p57 z+h4WWmq^Dk&XAJ8Nw6()L1ZhZ|3s^?49e-yV#5LCA->JASSr}njx>tu!N zgaInT3eC*o;z@Ps>#qln|8T$&Rr>k{etG*7uT%%VZGL*l(Vd<&^4`UreG2!s&g4Mn zl|IBsVIZyaXd1B}ofHBg%Y+D9XDe3V??K)CT{)3*Gxk3#;Sc6|%(ciY3*IJKsO{3kP8 z;TArR$sYkHgG9>K<7#0^TdgWXfsf>z%V?8bmwr%w%eca_TjykMb$(^tvgr79>;jb+ ze4DA~C*z{?qJfQ*q2U6YPPZ-_y6XcD723~+ENZDaf-}Xr*ii5kHSK=Whc<*ly7a^I z=J|IaJzqkRDZa_@B7)bc*#BM;_q6w@^>@TVk~9@bx{GOZ^gC1}mFV*o zjv9HSKl{TquHF7Mc;AWigeUjK{KVPb(k)^*iz;eZyMFeTSq)g^6dTB3?Vs;Sc}tt7 z`j4!(xmat(cq6j@=cyubTkUNHZUNR zC?dZ6OVV4%0s#2uV*s!*ak8^Dbg{5DrZf6anEHPV49*6QW+u)~|Lp7C>X){gqNqQ$ zrRYo0@sffQ5`NYG&ij(il)@>nS{lx3_Gbhf$qG?D))16n1Pvf2^1DWC)k_of{ z_$znXJx+XsOwMjh-^&?dyb>7orhpP2$fCiq^CGd|u=P9Zr$J22;}1A7D_F5R*}Yy- zDOiCu5R;0?@nnc*9zfpVNLes~86;yagMnztZM2bz+=bZt%+u^A3MmZ%1@+(568wRr zcSv92TjA}1_mRz{^^>gD=_jPaGjCuw$=bas2E{J%01?`XTn3LT

Ht-!i~xw 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 Yt+_8!yJD%W5zQg3qOOUk-l6 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%0AA!nz&y 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≫cw+>t7~YJ6*-%To?O?lI1O ze7=7^&+p?`7tgOGurW|=*M?=3t7?STOVWqFoH1$UL>GKqX=IB$=>WeI3G}27<#(wh}q@U-*d5OVfYKcLyr(lgopsT4h zP)t=y%^r_rRe$D!1Rr-!03?~P!+HNGRC(|>zjJDb)Nnxu;eF}MM-AUF2(_s}L2F>- zie-2arf`kMHJ+j}J82zV>um~OK|*jx%4qEpxUf2K8zUHh)x0N4kS4&Z@%_K|Eq)L* zuT*8jMaE-Z>Xmf@=A&H=>o4ZHr$+vFIbfLHz1g|C?9mHzGaf~O4Gj(N3_X*GmiX%B zY&@1NCgD`h7a+!*np1%tfyr(PQAqg4BjT<+0w#A{Nt@t0gk)pO8K!dT#)!^DBZ{hy zR*67C(h<;<60|f}rSPR>Au45IVlAy|htmEdxt%#K;VkTAeB`f%E8$s#RwGLtJE%>y zD9F{kXQrIyT?J;qA-gPHc}9K8KH07kS!-J~9wH5a0&&|d#sq-%jvj}w%X{=VSJBG! zbQ>4;G^p}>IhB>(oUZgR1%uen9KEA4e01)EURM^LZwtekq>?v^^Ig^3gejfhR>*8# z*_ApE$!zJK@heT4U4#bt*S856g;-N(!^+@R`-5U|Y>LjF6}HRI?H{W)mA#C2j*^2D zn)4g~|LzR!jDA&K{hLYG{p$?UM@scjV1L4JG)M+aIVRQ8U!QGWged?>ghSRAZRJXS-(OkT-p*1}mso9` ztgqEK8c8?8w>Oq!k?d(E9qBdpN)GkAd;PbIVSH{n)l8~PdJC7gi@oQ!yXAeLP7S*k z5RCt>@IIrD4$VQS>eGkEkr<5?S`8E#)tAo0g<-~%%IeEBbP)|PmsK=D*o_gFB018} zelTx}-^enC{m+l=xfVKT_C5ouT>z5A=`hyaT`!3@==_*8zj-8paN7XBW3&4G>wNh$rIiMpUIV^COF+>)@(&kQNDjB3)Ote);%H9wxOl)v{B=lipQ z>QuRl1;j)>{+|{Sy+UscEGPXD$ajm6CDi0yBSf1xO#y-FB~ke#6MN(FGM9v$g-#hn z^1BN3w=|8zkNB^=t%^FMX}z8*y^ayH3!2Wi*F726&&~AxYw7K-beIE6IGT*w6sj8Qfm6HI>ey_f)KcA1ht$MZ@Heonn=MI=yHTvxsyf|3QI(tJp1X4b z>C9XeU`Pcm)K^I0KL&r=(aON(fs2n8dZU%kfWIs*>5|7T`tQG}*;3zA@$YZhJbrO} zn0Wx_@VSZo0O@?dpEq>EG5J^Ju7o6$%FTC zxveCi^GXh);R1b7PC-l*N!FTLK@!j}QE1bw!PQDuDJR+s3s5I0TbqAi( zAJ$ngGPBc`+sWH~p?K*p?3epBnN*FKtM-|YaWVPsc5)yo>+{H z_~xIJjoA2`xU?g&0oz&s?M&x5sfl3j*Icn?fT^i~C8?rI|F@hX>1RT6+YkIbR$?hP zm$4n#ANCkAdLCjdqGD>F^Bq{aKT34g+y=yvaL+{*!vfq0#&P?y+lF~%9Hp21x>8=ny`?U&E} z8dc^`5!C3QT@<)qH0TkWCsejFX|!~YKq`!fi3uKb*ZcPly|kW#7wX@GDr7Wx|L3FL zSwR~&eT}AeMLgH`*}0^Z%ogYYNXmcS6G1kErTFXa*^Y3*f5`+xw9;ub z=9!TsOx$@etiAer60};Ca27JUx?+d^EqT2MZImjph212LNO^&QXwhA5y7*aiN+dv?Io=T;U%r&Jf6q(=-K zHO(ro<6`0h)#GeF^Xv>(()2_&X35oG!UeUjA!VxsBMdgxSYl;(z}>%a!&?rMPxJ)3Eqoa>>(;#Zk7Opyb_hQtn<81QT$;D^(sZg`9%CZ`6% zYwuZ~XRR~?CD=&3@S93{hnRdz9Q)~yHL_n&iH9?He+HM|8f< zV%7&wxc2avYv zw=)LvHPOYxT6}GKYA=yX*y%^sZ4efOuYZz~4euaPuXr9)9(IU^(o}XwlfQ`t{u{IB z3ok)&;|mCWyykFo3GJ`LO}*68xaXvI3yx12ie&M>nmjx*ya%-L_|OEzfJ$BsQ9-;4 zqJm7hvPS;0Fkao#&6<|({xpg+Gd=$1(h#fk29o^dmIC+YTdhgCcx5&=%^rSX7`bg5 zamn6oMsW**3gxM^G$c9B#uP3jL=yQ`L7hR@uX3TlupK}13fDx@t3Lk&>j(PxRY@rX17d<2xVf& zS8CkrCBVh!|B=i)36SUia&vL^aQXO&E>);znP5zl;rN6%X(+Qo-_3}X&LEG~j$Y=! zzOvyX5B1iFD}F|K#>@d@?^P&5AzmOMrsCZ(X{~9Di(2`k*OwEx|6GZaPzEcchHUr6 z3UQ1cxIeuD*kbY-XKHptl{LFQ3zsT4A?*-;F^u;f_^#x>%BF^Cx@}{I;Ov)Gk573$ zn2j42YF+{7iF1q6kwxs`0mI@68hjx6yWdi?)4f)x&&aaK*>kom^=B2ZS(5xddU%5#L17-~!+L1#5@R^I%=) zc(<{wfMko7^qsi1?NR<$NCmwcJH0fm8G4q{LK|VO)hFY2oU8!?bWDXMs8B90rw=J* zs$^U)1v-+40|Oo+D|5CpyD|c+jUMT~bF)2?_MMk<;L~1Avbp{T#zG+z&Z?Du1@ zWV#)|Ei{VJH35<&3Fj^;J6@$oX^4}4FFy`t>J4>nrY|H9FpuS>rYAYJ*8 zfmz4+R%D;^Vv{>RX%bRzXe{O%MeAqy7ufnYdmbn&H{56(clXoOwf&m zup5g{coLq+}Tz)&dXQ+C`yX zRg=S5M1(`z)E(P|s3R4BXSLT4QV<`>i-r&V6ifsPk$u~tnH!{$j>!U@(p2wC4O#IM zMh!e(AV}x%)*kL0k{mY!+7w1K)Rf_LaY=dh$|#R9*`rb?V=Vin&$9#4NIB2cR2LzE zm$f9?+s_j--iQRvrrwP%+-K><7!Gm0h|U5}@=s-*5RR636y9S&2)|fre}5I{M?R30 z)v=f0-Gn*p!XHu8RCwBPrx)_8uSLCkVpOFg;)ZtROu1nbs)n5NR+tuuzh}rnDu@JOZBW&m?3$d zl?q0=;M$Kz3j%^gHhBa2HSE~+;Cc|44AyrROF-;R#)uExV`xK^kLU>quiCBo&EFd9m~ak2bM(DH=v~fXw^cdcP&$@`mDHfKOmYy1>Q8nxI;6& z1I4TpP0qy7X;NVY>!GtizSc zAp*@M!`!iW*@X_l(5UVCN?&-DPZM~NbNXl>>c)rd`F!QJ0@=b73GPA~kKL5vEF##hPGy}4a;Uf$>DeCpP^lYe7{D8ppw2;JWFn#s;7hsY zxAm=EGU$i_`!~QvI9#Bex`16}xVQ)ZlFfb~Ju4;QKN|YU;ISgFCFd}UhVQHY&}izL z3CKkH%}TD)LtUt^`%)E{5K8aw$aYgiHAKjo3>i3^Z9yGoi2$X81iCH*o7$kW(Q;kY zH=y8C&s2)s{5LZ3%rvtRo24UaJg(Cr%oPXqf7ZG|lN*YSCi~jdOJHF*f8Xye z;(2j-fTiL0Coe~P)w~TVwjwpUqAbl73dCNzMDO6nu|y_<#DvcBwsiLzgYoLVMAqp1 zs5X`HX#LY)7brOSdVN*%d(&)Qd%ZOS7{2V@TMgS=QCG2yPBAh)}L&>8XD!&L=Lk%q1x)nh#qg~-+^ zfy4$m=gMF7^irha=KuN1GQ{)G8#Tz9{|`f2h#laf+9n-pu||O;@!O*S>s*C3LUmvD zA`ft1cAPkIv_z7yF>~x}W~`+cQdHfW=j*eBWeXp+X_gZ8sD5lbF_YS4$)-dxdd_Qc z#uMPuzkIXW#b~Zcvbj(^Yb*8e=4i(Xq4sKJ?nx;X1#Ar-8vX7wnytg^PL=AqoG9*2 z>D8>pfykY}IFwtn$npM1oduD1!=BHc_kteQ77x_&FM>-!g$)h61OTIF_(WSl3Z5l$I!7As}aV9sYpnH9d0Y4Guf$cSkQG$B0M%d=U%sA|5Lq%L5<->rp zh1OXLFo>#Q+_s?!XN(1z5r`3#697F34gKUrc4QT%K%|U)U9jtAcrQMM#LCfZnMY@w zpCA`$Y}RD{wQ;EY>b}RaIWl-Xa^gvFDR9@CTD{LgF$8>KPmWx_8P)hO&;r6NwBo%| zj%hc5S)+G8tJcmx4;u+&cf?wvGywgWC8R(F(h8B00OVO0zk4(*uZfHoYENJY4a@YI zkW7o+iy(g$i2Q5VW0|^5z2ylwjro9$oF})xoWpbam>X?l7f%DVNHUxea^o62WR zc{cEV0i*~d;u0`6I(0hUFnafSf7u{RMdMn{OSw%lh$|xi{i}*Byq`)krFk+w+p|Fcr5D}b z(Oh`G+iCtDF0bq3{^EY=3>6KHVbgW;k{j6qPhDt#nu6i=-U6$2$z&>e=|3jvV|)MX zjxtXyO2VBR6L$WZ=Wu+R8_D!#&#}cAH8u%zX;PyRmro7Jf#CRb5Vxv3PIj<1YUbC0 zlq22aqK3muQWQ6@Q?&RrUItE&oN!n&jbS z?gi`GW!l&FA~KwkNRSkrFN&tq1H)<+67D5NKhUMiyo{xemdNRpC^r#*Z_?aWyZY1K zii0B+k!k5GC<(0HWGpo=G0Fx|V*k&Ej%IR|=0VdoOHQ;oTN*26WXvD37VLRsTq;zgybvoRhe{n)C`fnGGz)1>WmqE*&2Dwc~O)$Rzab|k86v)H0?n!4M{6F z?e6PQ1X<9ogva=dU(VkfC?3d0tRtTEu?yu&_*B=eS#Ln;8H@4CD$ zKJskza3p;>6m8KxxLe##&{Rv-A2TNCu(v7&tP3XLEymw%&ko@HKi9P1_AXn%cr5&S zr-q5&#HLq^OLx>Ef;cUM#o=#IMs1@*T>f9`dBa;`+u|VF=*?~>*TQh$Uyf%5k%f14 z+#SBpVh4pGq@9@bz1J7Zf1lEOyI;;Ntbq}UvQwXp9i`F4b`~s^$r(gOBFl~B4XC1V zR66f$j_~c`+QK%iNE3y&JrKH%b5-2dZ?Yd6Pu^4z($La8k^+eOuYgV>3C zyYm&3FOnsiq!IO^qnxqM;kGEjZ=ZM+ww z5+dN1WTDLA{u5iaQEN#b-u;!QnCHW)N9aqqx$?Ynvn^ud7L8hMV2J(_D?^>7NXpNt zZ^$zQLF5=SP*n|hsS*&g5t{&a*HN=&?JwFW15LhUD%agcz3fWS9a%Q;l&v_(RX~nN z?Z&28efoQIrbmd(3|k>NCx?=LTTEO9)Mu+A-wct} z?Z_?Y17?Jq3?y!RT}2tL&>70V(P*}6yRSSR+LETTeWf?A0h+o@;`PeBC0V*QWCc4hpU*h!yi%nJJ>LE{zoA29v$>t;8&05{EwuYgrl-TAY;<-krS zo2I?Pqe|?t8yzP4sVZ@5gbtop2(CJ@D{J2 zH5-^L%SRh?C>ux`G-aX2Q{&%F_4d<0s;!F6j!UTg38HX7t^Z1QzG1%av4J0BRJpCV zS)-}Z08>+ZB~ZD+%)Rcq2P>w@X z3VJECkZ4m(EW=_1RZ->iIa$EJR>7=&W5joflXhy^_`8q``|IEeHiXB)v28gEXYVw4 zVA;h{d{0dkDBEVGvcn^>lBm=2xX?LfXjS$k^s$FLuo0&T*836~|1xPFr)l9a5-82h z;imDGxbPdi^CrDC5uRng`G}UkC3Q7{$v{i>07$g6XF@*G>coz_4O&X7IlNr;zA$H% zRls%Q=m>lIm^2uL5?G2VlZL<_F0BLfz$)&4k5#~Qm7&kp8f0q|FhnAq{SK#F!iR9# zPxyzm`O23grmc?3-DkVcO`VE&xh3~Q7y9!x5k+F`|Iwkzo-3Yneb(<)*vmun)Ih*M zG9MF>)16SOF4x~It2#k$J603+(cqQRo}CSqmGY_D_)zn2gE$Jf3BS_CgH=Uv@zmcw z@uF{1t7;HzY7oc{12bEYJ4Yv3hZU&UUXX3L^RG1Bwa_sKz#Ibl25Hn8KjBJKP@bf$ zgIQ*?H*Lt{T}>lQA~RgX+8?a46MPg@;w-Bt3f85AyQ&xen$P}$T7#d!?V()C4g|Sq zrac9x3GbW5=MJy*jMG-fveR&%`_fyCxlsOK*15{H%U+JlE%o85BXO0{h2}D~y0Gt$ zp0ehyIFXpFtG{)E^@N8KjEhGexjxxv8U4i}5@E$_I2Q#G$5h=$#oq2~MVf<;Ml0#C z*iuzeZnCj^upiJAx6!HZp3^iA+Da5@yv#~sJaX9*ca%3 zhovAw&Rz$=007i~JV)aHgXL&vrSD{7>tOv~RvN7=Z=1=0=yR%?wE_`_F1N}n6_F&a zm|>&bTD1_@QeF`U0%31xWx{R!{>qI430Ji7)faes)A`xc$!vOe`0}9omRw2!7qW^a zG3glN0iH{hV#ClCHq*_lR#vgnwWij_;rq&0Gz$^N4!Ju=0fog^Ag3(euNR+;FEWH< z^XC+pPNl9>G-kljnidbJcK;3~xNKs$n zxEK+YLY|q>B9FV}xFJngr$!k{JmL0A#xMauPhAWpEMc53Y0=d!+hhXH{b-!p9CHb+ zS~WyTvzg$r`ShO$gmZXezuC-F0T2JY#TYDO1v6QOxPLXfd%h~v%+^{P2}%##VD^x6 zNdD{7C7q^+27`UIa+Vq4tSO9wpk1Y5z7tOOMU|dMcJZsnC(pswCjq-$Z$z$=+Fvyz zji0icwLk6K=!CUSiIgk>SKmjlwa*lulK;6O(F>c#RDZr92RC}seqHo zUa#Jstp4YalfFr5MUKE5l8VHE4vOnkjUb*>fe`$xE(d=oAN!AcABWi!9f6lkoYTo^9jHQI^AZ;8LjBt@OV)r>ScYJ}4 zSVar1ftu!F+JYNtITiH=`**ML2Q^rv++DTiymy&B48&Eueg8@Ts#NB7K5vS8V^3(r z*WdCjlJ<@GRa}Ga!NB=zS9mRWC_D^vXK%0l7)$v6XsCZRSx0_oN2Ah?5I8A;%8rx4 zhdd-j^vSU17)JZ-%^G-nMpy0;*Jvo#o~Tf51t0XK=C(b>ajLw@RIPy3n6o z^R#D_A2nETWTnsLUjzZwl?1#Lbb{e9#=T)6Q75}wd)(xDQhEz z661y#MI7FdL^BgE0my*A<1<$~*30EghUuW#(f@o4fovNi9}9C!O2vm7 zBV=U_a+b$w_K*3vX$H*tOJ~2TtzW30n-=hUiEaa39@4YYH!@uR zSBx00sCaMiB$^L^lHt<)8rJqKN%zRUUHr(P3 z{*7k*tx^PDiz7c6K7mTCA#dtY<2k@N1Ee51glqm?i$`_ZK_k6o8SDEar8Q7hJ{{G0 znNWS%UID!KL>%0U(=hetc=mL(zmgxX*TeJSXZi;{5aAh33mu>sPeNEx z;gcxvsJvwEVz6~1bJMnrVv(BQAvRZ7yuvr9bq2rD%h~>dM)mzHN5&L3S5v<70*gw| zHgZl^1?vKzLK>Hkym8ZI8I?^?c1|k+s`=ZDFw_UGstkt=_1k%~q5QX=O6BtkTus^c z1FK}tmy4P+OmuuOZFFUmW!wftFC<{DNk^qp;y+7L5L=JzphrsTq_@{rzpY8*QRF*~ zbc*=&;UAiWzULoDHL`u_iPh+r^wcrJ0%Be`or4HGj;29$S?V5JmI-sb)|Qqf=p7RE zuFgvZc;Z!|=7J(kpq*q@F+|0`GPfEmYv9dY3Tq8sBNlR3f&|J8^DN=|2IA=bgJ3-V zzA##Y4$u#sM38+n@7jou>T5;L091IQJ!age2CFt~5jcde;hgtQ+3!=DD1VGKCn|_g z`vIDARYeBQdD^^W5GvBl;{7aE^@mmPdjoM=AYfc!=W!thRY@7y%FQCkB`x?g_Ux7m z7?LfNHb9A97BVIk2I-#{=kd(C&Ca^4!36Fg>#o{+6wJE$HCs`E zHE=aB^R4jFsD2}?gM-4dBWZM3#Ct^thGKPg$d;%F)^J&4OTBF zFc!aMNt`){o#!BT4|>}zrnOHK_nr^9tz_0o1LOLYUv5Fsu}aHv8kYM>GI7Jds*E3r z?yf!_UpH^x$M^Dgr-uj_wfN)u~cfM!EPo&DsxYXF| zVM^Ow)b@k&@XsgzZzSy?TIYVXA!;8Ec^>>LKZKg$v0+aCxW z#aW-H5jY35DH1?wWP0?BRRZ(<^_ zJ)r;OzAD5LJ&xXY+zRB^FmcJi6;hOM=I3G7b$cyT=k_;r0Cp_qU|Jr*-SGVjuDlwJ zwyc-14TxC`<~FZj@qYGkFxpC&gmj^AY+uK9*l5$qqGfdXf36GFPi#bLcpi&O z-P0P`oF0!i0nZj{5Ao|zs5iZgO0I{YjbGyb$~N2lHTO?^8u7vgTKC^aLF|0-{mbTI z`I&DG!CMf%eRc+S1|D=LKK-G9$l%A=rKfK=gjQ>qVWnb5`kDiQ?2d6qAVQc3U7prW zQl85(2%{I(3F`*$YQSmlrIoVXeQ`&wcF5RFfCNTow40pcB4`CtZHgZG1M=w7bVhE2 z;DhY_{V&lp6;!~ZR+-BClyf&P=b$R{!yt^%V0Rf=E#XMkx!HhiJ&%*Kw@D(y@qsu% z5Q(BSU;Rtq6+x~8w#bdTohhf9{gg*c&XUJVKD#Lkb7UGqaOmR@=7wR&Xf*l^{1yH- z>kc^90aI|6eYRS0^tJsX8B$#s%W!D;cV*=Pa}fdFi@%vT02O}@&d%Ib z{?JtZZg!QE2@fgMJCUorrs@X34qt~~%nJ9VmtgUJuF%$<2DwndxWC4lpJ|P&(~y@O z?5z&KH-ZZ%9OH6#daiMPu7N9V59dQx9Z=xi{AZJ0flqW|nm?S3iID(Gy^0mUPnV!a z$7N>pNJ;=H*lNQKdMr-jgADGKFO)3f-gG7*#HH}7fCqIN z*R?u)i0?exCCyOAg!WgZGU5Rd6hGOl?O-zzi!jI2B0fscYQG^)jZB7a7{VpW|CSEZ z|8*&;0zZXa+Uu90V$XX;?}O^b+(yA`QU$x-om8DI*<2 z=w8jDCUihX{i!M`q!yukL@aaJAHj%RGLKyc-VOobIk6-~fN>Tup%NgW$XvlHb7YQj zlXt#H@P`cixv$E4kDU0Wr@qY2Ts1ghHWZ!s$Q|EE_38aq4;RP0oT%$Y6OuMW1@iTFJPYRO&A-9fsQyW?PolI;IHH1EM zn~=xi4y2y3UPYc>xFR#8r8%esmFtFr2CYYNyE93#zf5*`ysyiD*CI8+`2Z#qtM@6# zjJ?02TCu$^3{~@DYz#v?>liJT$K8tCFjF``rMLNtQ6!6un`D!`?fuFC?2@g|iH zTi`>}!V5}w^vC+jjlFF8PMY!??el04CM}DNG3cw!7keGI*a~ULkC`TX4|MXuFwUiH zW`g-_jyh1LjZ^&Oj1;~r|Ly1NaV;WV+82Fo*bd4LbC*5q)(<`=N~bvwp?zu|Ksbm( zSuG?X)poEMt>e;;t;F&e&}1ZRygiadV1S^C5-j~-J{qf>A<-H#Z%WdEr*+<9bx0Jh zzipTyfD5|aGJ(O#h}UCBU$d$14BsD*K2A0^E=KMh4%^!~IoQA7KO8*`6joU)k21h9 zhfvEQ47n>V%IX+^E<#UnnvK(x4PX%fO(X&F`~9vI088c}S~u%A40U3Mb~k+cXQuG<~P|HQ)~^oPl9ctufp3*fHn zcjMuRdh#VLfB7|l@mpERxxtnbxR%b#8())y6iyO`BvS~<@1zPKQdHGE(Yvw>CiSQ9 z7q9LsgMH^g!FIY*s4gPd491(_lo(G$lKnGs&ZN*r&snf7-uxcO?JZev+iPpHhd@3P zyhQuJGV*|I%TW{Xes>(bLABEA9Y^*Az3uVUzVgcUk41;?U*P4=Z|fTZE$ne&yMGgY zR$=L}Q9%hnRJB-DileO>*lldPjE`m!4KGd3G@~Zyc0-w0jiGh@czu4IFVJUioDq?x zML?855EQ>ekiXTKJbAhKfM^TgFF`~m6qxxR$(8ixAIyo}QkK)c3H>XP8p-FCC}t zNEeDn#1s4e`haZw-n>o$B;96#%{cd@-XU)efomHe`9i|cxsHQoM+MAgIN1;bu2B*V zU2iiuF5R7oYmpIizaP{L?aO|ylyTnlgdC3oRdulxDk3S?dDmezSAEhc5*wRgiha+Y zSQI#tjRcPJixQcIh-L+GW0tcPB0n>rkFxD0&@r^P5KeWv3l*h4t@!Eh$1bD=WA2onK?tVN-E50xDY z4~QQBfDil`xH=JCP;ebwGK{7%GT7F5*|-ka#nYl$x$+9bt%8)!!W7P(G7n93x8FyZ z_~VI#ee?2d+%k6G)Aakl!03c(;PT1STGpBJy;q8A@^FOV#dSWefSc9}Y9cLsO-Nc@{;=)S$-iFp4112_Np^YHvAcbjDAmu~V8n%Gyh+1Be|| z8>GZ!b4k^+c9p55`k{#{XwiVvQy-y#K(NBxFm(Kx6OFji9+mG-j!?5-2UAN&MgJI* zo}?|JF#sKp^yO>&ja_q_!&IEh>;c^iU*c{UJIpMZszyBUqVGt>(nvjB;Pi2#YZ2iw z6rPPNR@m^2%FLPcikYacb|MgA6;ud1&27swiotyrl zoT&4tDvOZGrpqlB5x9LaMQ}{;guw^i$o%(TxmQ&#M#qKR4Q`Soz*hsPUN5AOun53D zSVuJ6DSj2F03Zwquf1G|#S^rc{HUzSsjhbW6X)vm^>CQE_f|SdXLdHB20{My?Z5ma z0phfcb98q+ez`cvj?wEJg+B9)7|udYy?psVWQHO}tNtPJONbMDDsa^DwN}vl$pX1tO z3zHL?U|kVP3Is{*et#G#D(K)3WuFi`DDLd;r&dFK>z-<4%U4cXnKzT=q;cGu)w7IVpg%O%oipmZ29+ zHO;EEl}ePWRR~@`Hpe{4nVO4~Y8PLEaBN&zzPx>U&>%Q$PXYfGY5(OG6?Le2qQEbq zqa-%8-nnP8+`!E;E{IHaAPk_1ecWJLyrU}-S(|IsI4a&|S4Rf|O|u3{*zT@IEbK;^ zZ`!On>zuK4$4r%{9`84<7VvZFGc;3utIQkfCg2O=WM}O8t8ssPaqe7;rD1Am7+m#S z)=QZ*Ro0w4RA!VrRd^QesW#Q_I+SdaAxcy6U#axwo!{f;=M8Bb8@0=7hfGc>3rZVm zBj-2tF91G5l3+1*ajE_^m|%e%ek=68DI~qzz9%Am#};#@xd9uC9ijl|p3%KhCve&% z3ZJ15@TK1tn{$jU-*CFI+2OgyLclX{2iUw~P5EZtNJd_d4bS23S47CeCEg*0MyW)v z03ikcrVO*^VH+UfJ*I1M&HcF>jI5_;YD3CC8-_J};|#sy#`g;ea7;CHiGj~y`wuH4 zp+u$Zu)5usdE5vjW)ayw0%kA-8OkR&SD11MWvVaE8uY6r`k< zup6T;d&g9l5D{1b(seVyRA}LcOAWTbFb>|QAEEf)w<)>g)c_L0M!57rd&yM#_H z>-10f2p4pTz4vbz(V%0cL8+uPpvU#~VmwZIoxkTzU#%s}^Z z6wx;5gy${)y~VHi7YOv~6yRBmJOx9R5s1}9xMmsQtbiheZX?Pq*LUw*XeB4uFjD)d#vNap;(%a30P()%bq{P%%}n#<6GIv{Lb}sC9TDG z@bp1UgH|y=@Z8c{mfqJFaD&1s3r`6`pKK53>JlROEA-bYPDHLk{TWMwX+-C;w#?Tt zyENJebg~wNd(P+NOBN0}XR5tuo+C!Io0C3=&N`|PH*XeVLpkjbeuv?ve}@IPJYtcg zJJ9mzMCU;I+er6~)rOY&0XU{ruB%Q>Dzqt99CES(7-V)pDenDD1VxmH(KEOnttjQc zV_KK8R9FHZLmVrwc&sDbhj$uf!(0!hl6#7lmX?y!^W*xwz01c;dGy(Y?cQwI=8T-r z^Xs95Ft9U#IB{{k&pr6di``K8=MRZ?atpm?bXlEP%ILeBQs_|O1X!`@yRAVSe_jb* z{rmZkZB<9%rZ(ZNwv3yWwV>GJ9tO(_fTwr)cTE-ivXr1A6Z|es8Rco+@k4E*%YaY^ zYgthkfN@yQ9LM(Zqhwdz!SNcuVO{CMKQ1xP!NfJ;$JLT1z}U2_duRl0$OKhu=czIe zR}E4wW*3sUUO`PdDvZtn@7%Pft?(j~!!)=^SacwFIqgIsMbNEEtXc(GfnL_}(Apir zDf%*HkKU>+Mbe_ZXkwc1xfALvp-r^WX>DxehBzS>?tmHi2-(+*JI0A{zwJ3QZ=MIi z?v5S1@vy^)-(R?Bwf3tM79j9j>F0jCLFzGu+qxMS7=>*hZ7h^%U}a%hx#`fcb4pCgIBq^^}pU!m`=e4cxv_7;xZFGOx|4s z+DGr>GF;4|m`Cy5hlPMNM#Tm{W76Eq6RetIS$d1PJvV}qqb%yan^R7BPCgqJjmw;T z<7GqH8<3;8p}m0UA`wSHGb8)AMV|tYOJICxgTI zuUYXmO{9pKPs;2e zwfwj}+&@k)``7QGhpXiL)-+V+p-Se#KKkJ}b=o;zn4?vwjM+T#&>ni&>>?RqV4@X{ zT}(_g($_HbRy0w#gYd{O{(9CIGqP@Z!=Z@wqsfa6WAqi@e8q|70@H@OuOk<>EtCbR z``V|^@T=`kEN?;UR9ydTh!fbUifFfaApGHYrMxhCf4c$0Cywng0SlE>F4sfDfKXZ??2S5uu~@O6z)}nCdKyvGEslG)oHBR^IQT zdxmrq4d!D%0A22nZDM6r8S}$et*P%_Oss3fIfvoM@^M}gliEf+C2^iY;7)()RzxI> z=GF2zgYEMxYNHC5StH+)R20MDK4Q{KVH^5PYv5s9DsJi#@F;cq5Ft4StmTa5Dw-lpSld)+n=$Y`ixv= z8-%)ql(^F8+|mq^Xoq0s9P|?_a(l}9{yx1&Zg1m3;9h^lBHm|<{m^8vY_EHNIJwSp zd^hepjp;#S^IWK1%wWuR-yh=s^7_7}FW$T2vAVk=d2zY_9=!XTs_ZrshN=5>Lskle zMRIBG(aV*4P^Ag4L3GcLsLIw7Bv@|?whAf;>NM~cz=iL|B{U@_Xw;u`<<@kwi=&n) zC|8`eooj9v*3r(SQ5uGrkD=RtgT-iYdYvafxf+U|A*Fq|JnA9{HH9-Kx_9=g7k5TKT8KAg*3u|8txlepv~ zgMb(_)0c@-MwnqHg?WPVowkaFi-@Pf5CRpG*G(OQAIiHc7pGfnCBRz78sd2|#cMcz zs(O}=7bS9x$3OIh)uU9<+fv3?z%pnwLY8$L0b`5I@B(YRsy0am5Rw51IDg9g58;Pi ze3UyR=%qvhHxWuyJ@bAKLx#@7rpqZCxb?_dq3bRIhq2#^RDwJOR3_>H{g_aI?%Am z#_nZP3E($DqY@d?{T8q9)iBDdxQi|}EaO9!KN*KfEmZ0O*3hxnkWANE5!u7pY&^C9 z`4Yph=+^hse~k_PCYyioDt8Etme_LirwByjr%%LA9v-5`a)hk%%=8i~axhcHo<`ax zoJ|>Es{7AP+gM6|$0t^*kkob#zAhMLR12e(S&{k3!|lu8&K5vYIe!m<*5aF^3}*u9 zpY3~mT;E>aH2iLU4kr6MdM_B(u`eGBKA+7Mp00tIk$HU{oj%kMe#!yD@4kTl9gov@ zxA2go0Ra4R007?qk^R{_{l6t*p3VPqzlPf%=%fcg1%)!L){f487 zjJq~X7$|8Bg~EwQ%g8ap#_k&Zrj3?Z9H_B7+j{z7#Sk(SONW1ttK#g~@M>%Cl~;sU z&XLzC%bqw=Fw!ji_$An5sn==or`IJGrwm3VO@p$d9Hg78Ofd` zerY796d9Q5<4BP2H_Azu7m%aQP2ytF9wW~by7@>1g>*~H08KW^EX-uVRNH@s0r*+y zfdmrTp*Sx~O9srYW0)L($4^8MlGm66atRhCAp9{P2rKh(lO?mA!O2o`5cBiHaOAm^ zB~_A6jWN2gUgwtDYgs5_nMi^x==exs|1?H@=6Mb~IgvY;9&t4OKQ9t}iFuxi{Wl9# zp?G{reU1;!jn+W6M+`RZUBG8BJ|qN#AxaIQGRws&xvR*?&Uq)EUgWwkolZPQ=2$bg zkkAQ+dCLXzn+%`apcbzlere(iuMyaq+}d}XR!8dZ>+=xzkDbHU{nO;k2>4<8>|-==@$#s;z}EZBuamfN8p9#$(TSQ#=XVytbER3x+Y>> z?$`R8Do1%NKZv6Z7!S#g^uDy)K5c{9IP{H3I6*4VY_>c09tM4wK*<9U4pN)qNY~E+ ztv-Zb=ukG{`a6=#tzr^}>fN)(%@(u532EdZ+T91AdXk9DlAzHXbs#G9?gUBZXc6n5t5@gkCRfs7PVY%B8c)pE;Y>XpH8@be%Cm%UzZN^gnmT( znOq$%#jgcqFNpaV9>}S_G^W?2e^4dj5F3y(#ai13uqLdWLE5fPF>qcjyv%oJK|eyM z1$S_ZpYwjX4jY7bKM2G;PX7(@^Vb;@b-RD32D%@oMTvcgkb{%3569KsIu93XEwe?I zJ=-Re>`odx^2&_)7Sy@BJ^lKkcgSt)`kM(PfZoWc;$J~k5nbe!(c$-8>S>BC+B z4LxW42~bmmNw>W^42?Xeg3g@7JqgR=lk)|8?KTG}!5Ds$X+e>0tvQY}n`HU;zT6NW<@+Q0v zKv;Lv3zs<5Ck!M_E^_cKZC3R z;|zWuG#P)d`aew{R*;r6ov8Ehu67!!{;`fi@0z(lz^Wh)By9xPTYv05u;7AQg#ifJ z0n8cXXLPxCke-KylD@{l{(P(1HNhz&yLkvQ8EKFH#{ZZ=V?1>q!C_=~L5GEfSp3(c zV1DT11Yx9sBVrby_HBaLSbXf1LD-igV9B||3Iqv!A?kM4y7cZRaNCv&+OpNff~&5$ zcMkKkuu)!QjGdky?%Q}kwwwTx7UAn8TQcSa`_dtA=rjcQjsappQqv1Cgv;1V#UA zlx@xu7T6`nF!qQzv1t~z7rV9o>x4xgymfbN%_64$;h=+5-x4VNa3|lS;P`OZx-b=_ zu5D#Xhh`4jj;LdKsFGZQQS~~)c}7!izbhC4yVc+Lqcm7Q5^_VsDnP-_;C=aNt{9gt z1YamAKU$RYk4YQeJ2`pi7L@0oeT4b+BiL)BYestu+^a0Z8382CwtOU>S#Uxznp^QFe|$x> zXfs73#&*$_?B8uQWL*d9%6^d)f`7+>Tc<=ZLaQ2^Xk#VK+a|*Tjb}tA>O(`N7-YTi zj8bB`8$(V>o;kgumi0_zt`ganA-5G$zUgltEsWB)izv@UJ!)hWN&DY~6kx&RFkvIa z4G&A6v`Dg2ANaeCn4lTu#sm3^5v^)tK7<%@KDsE>qN4=-U<=x@6T}e$+o?UG&Q&yH z8D(zdfGrrak5WhLpSEoB%!a!L2E)BQR71^!@({wb>nT$KYm?XoEBRqZzKkTG$NJ;i zu{wSTVTcfa+$VIhh2bD~iKW?9o4tH8zmquvbzDEuTEo$&WH3b$cAzkm=`^$*5nV?L zm_6}Y=OK4b=-iv^9ZV2Fz$0wcxJ!##q^}Ts@-uBFG6I>e6PU z$P;Z~Bc=Y%uY1=R#+4&brweGM>&5^T7NX~HM&W2sY>}uop;DB+58D6@rg$3;H)3i~ zZ4aPR3m{<+!(u~mlW>5)0-Qpjx}HSn9Bz36jZ%2zAkc6sR@(tovRy*QwBUlk51<@K zGhhYzIsBM~r^2%Gm~4}K^FEi}6r0R^sj8;onj#_AmAiG*}tDKhTw-#Zo>6_hAAz;QNlDStA*yW^x zd-XY4=O0K`3^vC1B?ui}I^|jb`O1ir#mIvN*bl3cTFd3m1D*p?xlM}CUpj`xSITMa zp%))cM~OJS(+b8DbFJ?0NTE9eb8H3;8}gwI@fi@3wN4-g=A0g=xWmx5eU@wwQ-4}$40 za4m2~-N}G6Fi7{U*gz5ni30QzPP}>_DJ+Md0i;a*C{L_LX#CAq%D=3WjcsItTb%Gp z#FD}aq}n=1&x}%F5;WQj<^TbPW_p&T4yT%xl#g!q<(2ZcY0-dH8#MQ6spDJa9_7?3 znR?d~q4-j|6=3}wqGX^joqv}NM7Q^jm&>v#PUuZ<9Qt8UFB`X0>}oG61>#T@39{w~ z-_dw-?fj?}a`fHg`%n^)o^Zh071`>El1LG ztilWY@Sa#}#rEIf;>S?y(L~ljvHT$9B5nu&C=w8p#ohf)iC|5MZQ_6dR&dC75TF{J zE2hL137Cv^9b+|h6QRnfV&V<7fEz+?Ef01bttB2$3kT(P42*V7jKU={^sruBPR3kA zv9<{}K*GOPfE+-Oto=w?D`nnh6+Eo>Tago)_BU$VzO*H}YiG{&U0i%5!&W|IE;1=G z)MsF|S_fBh+Z{R%@m&Q22#Fqy4bibl#lc+b5u0TlSR*M%;N{Bogxpde<>Fy2#Nkl# zV|2ttHz$2`lsxjc=Nq{vnVpM)FSpcO2)k(P7B0k4DE!rFph(7J+OozR7W@V;BOH63 z`AI0)J&1FJNCB-YVq5P38u%>qN|G@g$_LDk~bJv?x1~>$FTz0Mf z1{&~GSv)T_Xtkm~rVz4}8<+GUCtL;BD<=8g9f^N97}z%3b3v#f^zT&E&JW`WCO6~J zK*^_~ORq<`L($$9LTj|jOd)NSv`2wUxGaFRbtPaYkl54Xeik-GZ5u)t)!SgnwSzc@ zv59P_7j&%f^?tb@d^+Ae9bvhE+SrSa>bTB#Wl-f-W}q4GAnPnR!Yjm#)~T^dHt0%5 zk$-gCy6NbL#o51S_h&Ijf+}B-XoVXGlBSok_*9X&>PK0hyX&^zjy?A&$c@bB(s_%~ zGg9FOKr32MX#^ubvOQ;U7#68OyBK5G?d@p<$`a1S*F(>4SRFN)6SvmPTW08+P(s*b zzVlF;;I{E@mv8UV&uTa(Tj5l#Sa;S<78PEs?Jbxa~iRT)u|ExV%3&BE{ zSy=j8q8u%buG$>wS>w-LlPK3GNXIyYetpIRB_A;r<!g|r7QMziNe^N&SWtwO6vc2jD%`2<@ z7B#y;Q0#o*edyd}>Q{q2;|zMGc#pXlQ9=Lftb1o=Onm|bMH}s`m`z(8i=D*zZU2pI zjrb?5IdIWF@U*opd}>=FH5K;sZEo7w-Jb!yC5iOCq*`o7?AP;DYT3DNire4Wg{5gq z<}X1lhh3oC{GxyS%sxvlj9lS3qk36la&RqwF?}aS|vR%->04u#|jZ4 zqgq|=mBm4=n}ljXwgH{uJ^~b=7M|Whtfv!_iQYss?M<;^+Kgl*#qa>dnPq&bs*vSX zsF+qQWi_9EtWRSw-QOesb_+#S1S~A8Z`t=?fx1pi!sSg#&qHG(nTJtxUNwPyiX8@R z5q60)t=O!%lZaQ(yQ1~27Wf<6p{yQ1Ak?-a{ERyac-vNO*x`p_3Kt6qxfN`^W^MlWz!5Yr z5fTt>To6e*jb5+x^vqY{>}HWKI3lgFRpU=+X&>Dgu1V9>24J=YSX3&cS!IpOs17w* zO2a~Y5Um`_U(pgQX8)v@TP3}lBwUN4s*Q6FfvYe9l{7kCUhHx{(y0RKaemBt9}$zH zK-olmrg*qXZ3&d9GH`5oLla<|%I_gppA0@Ym0i_4GN^zpwHb&%%j!q{i``GoYs~uh zkgS#FiXYUnPdJg}90302kTmzE^WLI|T(TdvL!^zXP@=Nc8o#o-4OA_+mnLv&_we^k zi^*;dY)?W!1EKk5Pc`@cjmnzuMAVR}ZlC+V28Ktdibpt3jCVxkC*`Y|pmk|f)_%dFEOwyKg>XD zj47dbw&cG^&iPPWYvK7pH$w3;L0V9aVkqKsz5gXZV#Z}9_x}+emHphZ{%>xLPDTdS z2KEL$J5NF9_`d`6e5tvkd(0H!tu{i>X_i&oJ3~Bng8VcA+f0(T+2|ACLQaN!-}5Cc{Uj#LDz(is@8dd3Ww~ z6gLA=^PE{{)^o$hY^1gnADuvO?<;WgsUsRxXc;BtVXl6k7ALX!kc zF%je>i&B}HHbt(}_N56`X6)J3i0oC6yj!ly+?JZWdmoDBNQX&+DkFn(maH7tD`q3l$xf>0QX;E}J=OLr_AE>PUI;ro-M~wR=9{l#6cS{NH<@ z4Y7SYx^sEGKlV>NiRSh%zdo;)?fT7$Q>R9F_R$8)7tRN3s2;d--HjmYzBe!1G}h0M zrj!8GV@hDpvKt>2kk_30NjK*@c0|fMP9D$>IeImX1`e*{xw(LAIQmC7q2Xf7Tee%e zWqG^;u>3OT*Kr!m0vwph7JG8XUR}iWX1L{1K1=d+ar*)&ODro=pmZf(4FObV%8mPC zq!Q#-B-Qv4BvV_0+KM)d`PF!pH^(=1DvRvFbgU=F5?zVoOBLMdGys>dGS!`cm7y` z?tYNQL9mwB0sUt{i@s(iFE=;tnv0uvkz+#qL_OMYk{Q)=QfA2=#M)bKiD;LplMTC} zAhBo>sM+-^pK%_z5=d3MDU@lW+16PmCd{wLM7m?E1dR>)EWnJb{Ghx&gmd!cAdX2y zfICBEaiRy>n=08PlW;&dRpn^(I?(qg!6Nuo4uI;Q(qBhNa7P`*_#i^hN?q1AS#v%e3W!n(i>=5D|$mP+tv5*@rwf(qd| z(N(NYz0i(|q^7&0@F&@_pPJUFv|#X(Y)Z%(P-s299fq6d5uwYNFxP3CT%F)DP5(hE_&r zwP`&agz$nyBfj_Ak!VL@RX4?9b)KqZ(1;nalPTZV+5c%SCXrKfiwMM^5|vUUYG&vw z6bOlhJMJ>GnzJRvNX2(Nt;Z3l%MrM~f3TE^Ay!K0EFCkxE4AU^j5 z<(z*D4_p}=5l^kt#ic7{-4w}LK7I`&i#HliH}W=PHpvz=dY^ko5p$*yVED_>Hl%$~ z@dpq+Ft{H5i<_t4oO>+``r+t zb9FKN0%`zlW!iZd3jw%-&YXJ@#VY#K!K5#a$-JSq;IRI5 z6SK^kt-R=K4990d(YX^lvV`+0Y`4A}{oI*tq^E8o53PV!OZ36@T_$<{P;|##r_69? zH7UEc4#c24GK)6hg2p@V-cz}%tL)N7ThjT8AJH$dE7&QBg}<*0dpoxim>c_B z_8zp9lto{6IM4{EmLE`OKG587|K9igs4W^7r%CNN-F(N5R}wc2wDjR-IiWK1E6DdQ z!DSnXyTNWl-M?QaVz<-ZieXDJa+qrYPQ+udv@5^d&f2ExB+ua=bf{aLJTqkVR9vv| zOoeaDXcgljYGx9ATBVJW|E6UU>_rO_CDJos-(E{~l?$lBg!)4OtO*zIzG1hGtXDe6 zu5IWSOxbx_v+Y}`ZzE19k+4&7ck)71mqT$KEk=qmqHy0~#DCnF*;Gu+k8Iwqo*Z0X z7Q<*peE{DHoMx(Nc_8E?wok(38)zyGCP}!h(yx_Et8Di9S7XxvrDolc#f3nhR5b)q;$xb4odx5G5| z!1@24ks{J4S~Vd60Z}vm?=w=D|BGvx#`!T?X20+B1E;@+uMjiDFI&9uknL8o;hcXX zj;k3uqr^!iA&y{^s0Xr;NE`R-)$!lkY)z!-UL+%cR;R`Iw*&q>KD|8Nxzm2LAU?M< zR-|yi;5`vHM&!DMeub|I>Gkg@eOT+7K@FOd$NjDm((g-*f<}_QTp#)FFE6}0-hg4E z{I9Jb^rRGqu>nL%2aRM7UovqJSe{Jz-**NIeW^WC7}2O55|nY-Komwez|ggF$Qu$s zaf4JIEawh59MDhI-QZ@#4OgO>#n~I|cZ2D7!5j)hD3Hie-QCvF(4R3%pEAV{=`3lH z-t_AuzRhH@L`l3M+l{O%Na5De`w5_awWCjVymr^fui8u;k#yPMSBYK|;7F#Ow9)$< zu75=~yJL5(uc0bebIl>F<8sCxcsU&ZuySm*v5v5$LNb^)8s<_W%Cs_dI}53MHK9Yp zM`c*c{dT^-mx*K81E2yxX%Lq$5B(pHR7V8&)K_Sod!Da-hG zVHg|A__pJ0jMjyXniWl|0TLsy!z`R{oG>JwCgS0u3Q&x>X+|G*{o9(fb8!{F zptc($t9b1JikXg0M<-q)i(e&i&7<>wdm`7vO9*|b6Ju2oDlnyx>Z`c2GdFn~cp>Qh zOIW93PtGEtoT699@hFy~;3+}(_i^O%bYtQnvGcDB;z0OI1=+C@y&Ey0{zs-bfN*Wu-1kGD88F60UZ3IY- zrxn95nnmWm8ho$CAeh_R$~D(m-hamsyNl+C9=2bZD7;QR4}Z2CznR z?wJU+D$Oj{S)DrF_7cws^}q7#GyT?FCx%U&!Zij6~i zsRQ*N9~W777(wP@vO~m-GUy0(av0O-{?}j8ve#~fj}!$C0o&FO75Pz#w2$4A{HY2s zJ5<+HdD&-BvYUS?fQk_sUdp%_zAQrc{+&U4h4@wr~NRBQ5ygTZdwXMXa^4;&B>!@h;>5m6z?pGE~5?km?f z+lC<^Ed#TdY7xuq-QZQqo_g??m9B1Jh5D5)TkWJrrX*j69f{Xcm^D6RLO_{`SUUF$ zlIS!Bx0s;C=$8g>gm5Jm85k8<_8DmYHZuaJqIsyaW}ht?O)+}FB$c=9z#kR5$j;yr zar1=c43tr)@l$koL!*@p0(=aPa%b2f9&qFzTh@+H;#0WG^T_)-%rRQdIg3SgLu%de z#ndO>R+#66-p9$QEo_<6B}yI+R06WRL<-{z%EYr=$(_e?jRHWF&buk=NUQcrbv~Ul zIPa&Cv07@%Jq~6JplYhT^l-PAimQzRRK_iu!M8y`4GFGZ;=95JTxp7Qds_M!-QD{i=(7UM#!~b zBRIJf-5#;r(1x69={yTp)2O=Ea~L)2<;0q$@q~Dw6G#)OTZps{mp8%`#R6d@8bkrc zMGRue40I}~QG%!}M-<_Kh)6{kx*_*35uE%8IWux2f1nti1jx!VvM6dl*=Pih-7$M=y8r@_2|_SEBE5-efZM@thavw?wpldqAyeu_j0K4k7` z6;enBLv16{z~5xo-UBQ$htX^>8Ok10|*7?Ky#c=w|~+$T(OXRTC2 zUusNo7(qIJjiXhpm$U6?hS2BD{wMR>hqqfbXFL1n7D6zx73bu5i51JMV$7RTMJo|iz5a*Q z-@5j$&Kd}HZ#Wn08b5wSTYrVY3tXb&Tn{!r0e8YLxADw>&{tNbuKkTf6i-n|hB`5| zq2O7O<}-cJ_WbO)*v=4&Y`&K^f|q$^QV;7)QlJdXiBlaH6NrLXU6AjaoDk$l!FL1h zs&tSA%HUYMh$s@q5irCFUG?lZN+uG5ocUG%@VoWpMi08+5)Z7C6+N6zPmVt7mjIKn z)%aF)``}@5Xqv}obs8on^BFLKq{8!!c`fD1n56CQ=*<{t)eq9j!!?1SsW`0%}F z(sM?djR(3XB^j&MLaMAY|6mwlmIO;6=*Z&4wp;!p|n)h?e2E?ny|6mw!QUE zAdY%*R+k^1{gtc8=rX%glFoo`0_}ERyHPD2EK?v8QI!Z^<}>c$9h6#;{gRb=ggpp*HoK1F`#lMZHv!At?>YI zX}Y=gXdBjoIHHkVbhbKNm6tXTX7Vp<{1UG1-o9wlVX8zu+8?JVU^sPKp!UW!hwyv6 z^!aKkGW_yyff(48;563WGQQ%hqt~|wTj;Fld&3ao$SJDZbPpX63qJw;1o5JXwT*Xe zwbpgxnQ9X7*ZJBAy}9G7E`wnvjD7oV4KuND3a#hl4X+-VAnsoq$F+yw-;KhKr@91V z7&|&_PRY9aS6L5D{Utt5ju2>U6tngNJvh?3X`K!mY{^I$9TLBud+bjJss)!jxe(oA zU@OI;&R{L;LCH_G>xpC-hr{TiQ>AhY>xwcVGo$53029>AG_c3hv?wvYVfrnB9%^_T z(CGjuuIAg{`qmTo9K*28=gV`o73_^$t&P_4g735Ah`TQ*`X!u-hKg1ef+{FG-|8;- z?(q3RWn7{2>1r)_<lq zZ`l?WN>rP14Or8w5Y+en)&}okk^4Q%Y;S9(+lpph`S!Ov$bVrsF%&G!e+~cmHZ;Trpa? zjlF-hHL4z7p6ru;o7P1C(9$iwqxYAW-!r|!r0nro@=xZxW-O;BBk{X0ljzf=EQF?v zf5ie%lJ!Ez%Pgbfuo$|>%j#IK&xlS0`iLkw{HZzpNB(zgz$qxz=l*)Tv#9HLShwc@ zkMz1=f{+=kMo2zhWdP?eSMXx|_G%)si^HFoir9teGMQ2Zs8w?h{kt`m0I?AHWM5KH z6o(qWxb98L)a*l)8k9~t=tmSD-G<^fAtS=dB1VK(ce98@^-}kcBVpg{NM&~XB`rK} z*;Bw-sVxq7_w|@f_1A!KJ5>QMG{xPy?8)6I0nDyjd;dXE96e=R-*v;)LDj&I!L(1`q^KDeB%mJDtR_7bLBu+dyK?ZvdBI5*Ob)cgm zgb#Fq2(@D1ZKAdnz#$Ah5nurorR-~kU8ccHxEhxLIxptfSQPH6i9AwPIOAHL-qIh} zGlD_Y+UhoBJCRkCo7BJ^{bBfwo=Pk@h@k`p;Z_{Q zhc_=?HeB7q{Ndg3@|!*LC6*l9o!cvz^)~OFo_yzdCTFWA$*?CnMLZx2009 z8EAz|@WB(7A53pVAGHjsmiWel>=u`KmSHK~!(Le4j;A6^@iu2_ITpyfDA*(iIfo~O z*WKNLGOXzoKXtwK_omysY=^9brdojJYbtBCn8u#K2XwKVJY=G%O z*Z*ljwE7xv+x66`GK?arVm~+Exo&lEdAacP@Zfh_dZ^oOe>k#W>l&}72|XkHo5oz) z?uIdOm!q&4WZ;(nwa*{N$7Uoqa`@1Cw%Syv$&lrh_WBKZv!6>iAma!-#W=+Zf>rAp_Y*xAu3FpXIE#^zN^C+rXCJCJ=Wqn%U2&s z?;RGZ70b4}XefUpuN4%jkI=Z0@7_(|FonCBYP|`g`)lhX6(`&+t(1KA$;OVg7@mV7 zfB+(&1O|j+p9Y?3X<28nPwAZNXnOG%qkR(ZpgYm|sxruK(NLPcpW# z0M@lr%!)>TF+cGdb;{Nwh%OFMvOBW+bg}|| zT^Ps8iiR-!Nl}Wgt2$qZJ{>a|F+9>DJPJW2=Lbugb5z&lMp@)29cB&l&Yh?_FCH{u ztWar~IrQY=!GD0OHA4|m`9xt*b_-ziwCa_fOm`PPK$IyvAHOpV;Ss_o2CJb_o;M+& z8~99oa_OiosMJgF+UKo(7UM^}IqPveaokoj?-9B|*A%r_UH8tJT+Q&lU_BL*#$;%* zl+N-J>QCI)3Nhn}eGjc4sevh<{|CkUrH%AOTVSg2IZ-E9)%B8c8YcV}t-Plx9>16A zkKpYA3)9W1yCuO{pS*Zi*nb+}UGl2*qdBk~gD`lENCDa7 z2qDHs(-??UVO=}PZ=cRne?cL>LP%WG4zKsucdXhmc2n$q&MNh1>9y1$7nv7Pne%K` zbn>TI72`=}$z1jB$*fG}=2~pOSLP0qaX#PT`CNxqyJrQ7Zwqw^rHdU6@Y-G?Lf_fz z%NyC^oj6o|s)-M~%%v4)F7n*&Hl58svI2WH%s3JbqPY!6>oD2zQ`f0^`v8m?xwX)s zniAfB2zc``y}DE5F<0OSm&VnVH7QP~yWI2M-H%%t_<_o^oxM@l*?oHE=paPC8v$&p zZJMCJh}B#bn}oHMIN)S|L29bfp6Z)GSPr1hSwAF>jL#Sj=^)L*55N{ef3!xIDIouf zy8~^Bhw18zHH?bS=xuYbTb>+%@LgjQvA7sYHGOYz&5?&O(;`;X?4a+XVD&m%EiSQ{z}9Ab*pttI7$Yk zHWSx!m2js{utAj{7Y0C1)Cw>77$4+Eg@&5_>}XW$CDC>fHp8e@Bf&DoyOL*-5hGF) zgBP*Gr9UvdvPgNLoJ9C14)V+ASer`1j1jCm1!0{1oKH*x)RFXX25GD>CCIJdKfo=2rV*NAQs5BI`&_M-3w|Ic3SbX#N3*<44lm30ZaJ ztNq{uMIY@&GSH0r&;w{Ni2f)(;*pW;7GD)EZlZ>{iRTIE{#(M?4aowp7UaS3mhk9s zM?m-dfn9Mn_5nFZ@nGcnAUi};ilUhlZ4TuckA3$LAmK=vlD=@}*(#jIXmTv!)RFm% z3C@B3CCZ%4$^=gSqXDtw?x5r#mtnsq2(d=V+yJ%UK)a zdX7+1NPLQ{ra9Zqr;6-CdoU=3zYuIlq-gy$AtP?YTMKP&@d2}JW^OLE8hB%TOb*H# z=t(PvGR_C;=XHr{)Rv@j=-s8%uw<*Q!1TLfMW;noh#o*RKnYI7t|S%tlIo~QB5Rb6 zm|I^5Owpgy@l?L9Bss^n_TV>x6OC?88`<;wEUDR~gH+>B7{nU;fpqR|#jc66l1hLQ zoFAKtjNu$i^595S_%LHpW_=|m$^@6@)m!&`{_1?QRWNd}_BjpRS=PUJ=Jjf-iA?sl z$14$XwAP#GJAk@=dJMW06|MDcutka-dS~Zw#Es?*_5nTFy4PV_0v##u4G8<`OiZW! z34@H&tCvsgGCW~E)^=AO(>+<$3t-A>yo)`f-@X8L2QJz8bFC<8KK1%6L>7A~rPTNL z->03X4p+W$1hrGw+-?#l9n1aaAc1@p8KrcfeXhv|1RNX9qMgK$=?K^oIk(ytEE? zw;vbZE)!Y{U^PctZMe#Rq1T3ZVUw=k7?!|0Broc0io%J7NR&Y`Mj%n=c)xxzHT-Ew zqEv4#Uxr#@5C3PdSNB+>`1#)Q$1jGJ57ODV=460WMd`pTRvo(lZ;P#=9}%d`tY_{* zWTg2MfgdvanJYyJv}yti7ltUm+J@?&Xu$p?Tko@^A^--3QkS(1|FT77I6xYIn}B-{ zt>`#IH^H$)yn5pRY#)-!SN^U?=QNMS*Z>gK>H%?u#wBXDly;3#{-Eqrdd5yqCo=9#c0P#Bw#3xDhrK-x#A%nt$x@_+H`Rm7escz^+q9`b-4By7=a-Zt$ei{lf<*)-5 z-V0qw>CU^HWa345#=7P&MW3pq-vh`C`N&=zepL^wh?5(7c1>;_QH~?mp~OnD^6sfu zWu_u`3fm~-bo{txoSJdhNSs;Mk7UHPkYjOp%u1xxdGfY0^+E6(3ob_&VwNbSATz_S zl#mI!z?>R1j`>a6P?#)XXru!4k&aS9LNKlkHCQv}`@a@M^noHdB7uQ`qQQWG=>NZ( z0eJ&wa|v5hyZ`h8N|Uk!0%+SWG`7@HLWS4c@b&?cdKuVAYBC`T6s_epOccf6*N?y^ zBor$$EKb*)PP0DFIU;o=(nqos|H!0=M|DdLjp!1XH(&DIu-c7d3#6`naM;ZqoC0lb zQ1FoWGIREN42((swBaTPV8h~P-Sr?((*6-g%Kr_)>pFoWpS@N^t@t6qSTHmUnK#32 zuI$`)JA&{f8nn~c^hb4ZT$kqJsD8d4FfxC&(=YatC&s=&Xj^s0{me+FY$M9+{bs#P z5~3tTSd~8QFocOF9I2%J1n+N#4QqA&DjYxVMZ3z3_?vm!zficXTcj=KSm~)9U%U76 z8MIv4t|ELzOG+VyQ${1%0VrkX5+6-IeJ^z~5^l-}zgLTTN)y3;2cd?QqeqKn*tWN06xe`CZJbBtKA=Pm2a2Vn2T(v7RD5K!5_UoCQME@3{q zKM-yhGHA_e3SgW+SyyavjE|XZG+>{Z(eDvz8xfJ~`bS@sUf zaHRB^!?6h=t-->DgD8M~OJ~FQ^g0q9xz`rYIK9Qc#(q6@Z=K{gp*2UqcILGP7xH%;t(!OK~OqH#fTM zP{p6C0rgMZDC0{s_;-s+_t>Yr^0}9R`^d~eFTU)6tSGf?x>;Q<{~{jc#gva_+bur_i0$&f4F zE7wi-)F1aMLopShcVcQSpOdk`^-b&8IuitDi7m zb-$7b1nM;=wk`f`js*k3e9_%v07~m)alCu}OQ@F@tb}9scyZ`I;=uuH(IKZ>`*9ZN z`#G?^=^=)Yw5)?OgURX9@bWRd%J8hq^D7Nja%YY0y1wTX11R03_DiUC!W zRhQxTWt}VcL;|HEEaDyGgq4lp*sY2&7nPqhjT;YqHAP3SZjh1^6T(^4Xm5tKSmSLS zB?BpBHRYZ%W3thrQ9A@#hUM+{V7p3f^!t{6wks*Ej6Zhljf<~rooj-IuF?k4qklh8 zcDGjoggPs@Z`(OV@zv|fOj(sMTjv}0^;ro8oE1NT!z0VsmuvR+;sW>CCXCfjSAlT! zG(;J9WZy_8#6Ogo6i~SE9At;!rM*>Sb|(r?M1~@J zg5Lq)>{A1qK8eQ_)WRdzL9|2(YPb4@xqnTim*-%&N#71<`VQ?FhlF5szpeF21Vox-EnF_BdLKhlZrjR)2=(Q++)*Y+n!wFJY4X8X!QZ~@Pi=SpsNZR z&CVyoUx}I37o$Y$=b@z*i7<&W*{S+Ml9Vm*yCXrtywy&OzMRff|5m)R>MTYC95*3^ zpemK)8-7G8->j0hm541neuaFmm^Eg&7wFq_1Ll`zS$~u(Umo(kI@JvDvV&JE@^(}< zH7?_30B5(7LOr)EcKvPgMZVaftV%kvw-0ba6^x|F%D*PrKH5u%;z$yw#1WWQ7!n(L zkr@9m1|CsNVIE=bB)vpIKo&-Ksg3V|U=G50&tPwkZ1QcZH2Ip5ic5{9G)s;^qN2ShXuSO=W{z!`*pGXFN z7qdI~`{YF1yg!Q|x7)+Zo0;zyq;VX2HeOzjDSuv7F2f=>`sY2-a=3aO-qTV>IPwpz zivr#N? zoJ$%ee{WF%C?^2Mi(%HU%fDjWm#Md7fp#B^RfiXgu^!#BQ_0uo!fcJbBnV`_T^6_a3*-fPzRbP(6{&DcG>m~0$^{$M@i=j1A;hU z0b*;zXJQTu7GrGa21ne>$@TE_a`Sfd@TLaeAsVF`rrz^$uO@czIR>TNn>m~OeQ0Tv z9?B6tlpuz;^yT60>+7mpF4-AUrHdzS*Y2mTAfGF`8fIu5Xx79F(t$-WJi1wdoA`s1=c?<12M;( z9s?k9!E&jOSJzvA3z04`Y&{gLzB{=@6YrF#kYj=`xrsz3AGH&iCq;|NhGrkD^kCB-1==l}7;>-EJW(lIzHgL8pPqSs8oyl2@6kJen0AhzgA zY&{_(7m8SRW1KMQHy!fgkI^HDnZ-3PhwMD{{bLp+)w2-l0;1E^b<0W#)C|4MuOTo| zu2gHGK`Q$u51viK#(%p)oTae$;m@E^zHAaMv%*LPQQTZ&spd>5@F`eP8BRwxU%iAa zPa85>id++{0%N7(^LWMjg;WZyWdZVNk`ONM?+BE!n_|?HOgtGF3NpE}CRl{cllEuo zB+&G=1e~TtR*Pv=>fo=f9+sdxUcGd?8c}DGp2cdi+NUh0@sj6nD5$mxcJQg( zHPrl7s;Xbkr-6UzKfV18`-Mq%Hjy|6<-L|fz)2Xi6<1BE3y(-;Rf;v+3abD!o@u}$ zE){-da$Y9uhZS?BQglmmS~wLkum*y_+vFl9v_Ba%Ry!4PtJ$p^ICHJWjxMM^Axclb zvs9$50a3LvigZOwlIcrM05Bb;fVoTc{X&`#2zYc#mOW$oICjKz z%Q&)`XG+SG&O>RJS`BD-0v|kRDvz6hZ?DijCzAfNklq6pC=X{&pwC5wo?nv{0 z_mFz#^c=xh1x||>qoOmBlmfu1Q}h_mRwZ1UGM6s&7cv zNx%#o+svw8K!XC7nSJY$_^1X(gw!Oa5TuWxSnM(>GMEo{B`AqZv}6vSd#^)P zRJ}`P)|ZlIG2pt}a54gHF?-k>anWZI-tZtSyO#v;;@H(_Y=_C~k2Jc*$RPSYiRm)e zG#;#tcjJ*6)6TJwm2rbD4HbOEW4hpn3@=Xzca7nUpn#$fXeMh)9b$)chlN86`PzSm)vQ-RcRRwFejzziu0B@ zc$85rPVo`(!^cY{a^R#z@RE8Km^Mf>C%E2+qTpeg#NE_c0Ks%1qF`Cz#*#1Q$#esK0!11e$(5Pknc92LAU zdhCqGihKDXkR?M7Io8YV9Xg>HX8S$=fHRuX(H)KgfdJsW(o6+aPpvgu#*;Kd&%@h6NsU&lDu9UMfQTG)TOGm9SVZ&x@< z^Q2q1xggSLr>{2{6qq$k(3=a1Mn^bLg^rbrt~Bt6B)H|wmD?cmgyjztY8 z2IJ*-oAyjClbQqR$kA|X*~5K^4RsBj9sSo>n~$)N>Z8kuD5=pK(NtSd7MZAXRA!Zc zv|fd-L^=_RWsDD0`^v`H$&bGVa>QlpCYVtElK1|>7g8Eo!!$=}V_1C(eGDvS1EL{n zMur{wJ!m*ZDuF_XzOTX<9Q4U<4{HJFi&_f{Q7<*uf{`9|B5`7WVD|;YxFdiY2O^AA zJFRU&;<*LfX zHC(m1%R0c7#|YES61on$!BN&4FTVcBm{g#AA(uL(uj(oFD#J>( z7#{ke^pRk)vWTBv4xsdT%QSx@jU5xgHuS^F9W6M0Ok9P#Oshmod{hgcPz=nF*PIM3Y z;>wn@ctxFjrSOe)rN!_o77mXzc!4IPED!_nlh9eOs&`Nu~`4kQLb~_v=C4QiLL(SOS-|nE2$hUu&=S;*Ujs`V;_s61h!FR z3K1&Qf>vS8V$`C)1xdhmfj1`$nI2my$yA-TSgNnQmOgLNKuDb2HhO2%$6c^IOXix|riP~+9=^=(+_pEOu{^BN zHST9kFPVi{iTZ~E_30w^Y(?EZ9x~qUbBl3yIxs)vmfc=Rqm!JAvpucjSj!WKa^TY6 z{ea~<7~ekMS^dTxI{-|N&tS-wAEaQgFHK~~(^qw}hjcDmOYZF@tZ%T{TBeqXcYDR7 zcPqxU=e3L~-(_1!f%%C*^%U}$^nMP~nQh|f)?^XAsvuDq?i ze8G5mhu)+)cpQ1CqNe%`81cZy%UwNT{@ug5IdkfOg?E7^Q@(m9J`4Jzif^GP-sxq@ zEg-UTfHn?Uylb9Pv^_tP45oFLx!yqCb!oB{Or3cr8rVDYwcqE_tDXTQ>=ZsE>1^~$F~^P=Cn{9An;I^;@P z*h|~iQ-bC?af5W)p(Uguk|JkgP#^4Yj6#H|0WoFhhGFDvWgE;SXxfy#HDO{y{{o(3pw9!DJG8kI_dTnQOD34{ror~hW)sF6?ro8 z)981GtOV1cHVdIVvHZQVW-!*a;In6x5XIfKF=$3hUWfQ^u`4~txaIlHplvJIqMi-? zI!|Io>cu)1YhFKI!5a0C2|A9$6fgr*Z2#qttM6t)C(;-_4}ksi&!W}aT`}{JCtY6z zny(`wn-Vvpm_H4$c{o{YBsZD$yUdM1L=WP*Xc zujsG{!+xkMOfW=`O4@kri8c40HcuS_c1kP}ohm-mDzA8Vsz{1@!v{w-F6k=4AHU?7 zX5@7Q#9hRbOPjXw+g;nnO;yz!Gh4@;hFZ)~y&IV{I92ekDrVKP9(+Zej1yMJOATfq(q!%m>I_@soJ{BiWHy5!_LdF`S(buYq zpYqJ8dFpj>Xg@De9tx@OU(z(Un|iPVh?STIYqCcPV^rFpJ;OAgM&leZZ)|>4y0;bf zcMxS+hcE<*Ec$txP0 zMy!$>_GqrCLq{-x@E186!9Bnw<}@1G6UYBTY}7Q!4WSpF18K>P-cV(&f30RnSrvmC zABPf{U<~(6mP|lt?i8tIc8pQa7|_15lgLv-y1_0!ZG(;k9IeKr^dw9h2T4|0q{(Ik zl0J`@G~*$x$1ZPmwfcB^>fl<87)jzN9E-LKf1Q!Ns-OUb#LOi;D7iU9e`WB+#S_Y8 zIW$O2mW*$Fsv2URF*7D)v&|ryK%(8LmNoBbq3O&m3Rh;^>_sNQGb!LbU%LS9^E`~w zZ|veH*?4><4Y&Cd-sT#hv~ggxv=4{ho2Ak>6mCCoivoTG@^S@mb{v=v-$f~%F^RCltpAO$>TuZL{PogrJB6C*d! zPKb7NbYSST$5THa!|^a?5ulUP*JO@w878X)0x0O;RcQ@Uzoc4;ClN88Lx|maVcvTe zPS-2R!l)$n>4k!Ao)9hClms5N$4-Kf{9oh}-f)ehQF3maO}*xYB?n<^AenNW)Dgr* z^47kB3#;XgDjgqj6eEPtUhpHwys@-|j=l!EAr!KXO7Ji2gD`k^>+|NZ=M1(k0#Tgl zL>X^A)j@{>_P$uuzl;r7sHBJ1^`Xn~Qr@t|N_{xgjR9~xE^yR;&H>o`BJ2Mm01MN- zID2lgkxU-dwrOtsh*jhIcqXng&spP{V$!-8M;~(@jEX7iG|dlebF`p_ zlkc++`jM!OjLbqsuXR?+rx9O%D+j3$gW~*h-+Y}`YV0Jg2isJ2-3y{d*lN@8TJ#j# zs;C-9FsBYy%j*I+KP4Vupe3MZWe1J|#ad+D@1gOn?L{Q2tzj%K-wRL?K;k2< z>0lsK6P3D~VzF$RH<(!V>#J7|!JvpX`&=Tj zhYd9|n>9-&ur)GQ_)Nc^&mCC2ed=LTQeWSVL;2{QMi!xdr9&a_lbg?DgvrW5K5 z3H7~*m`^ob!k+->%84?VI0Wph-~VJ#e&R-^1+}2+HeuCRA2Wee~OQ+&i%?; znSv3Uh$a?;A!X5H4LK_ues_ItXhdHj!LIy02{3L{l&}cx^whBkR>#2AI`?B;sp+Id z0BJ<*eSB{+=CH}aCo!Zp7`@)#>_G#4#PAJZ@9uAa-7v8{J1cEpt6#jzQ_Krwbx=|E zQ)6R1p4mZDC$h#q2Lxl+BQj?{N!yz|@w%D>vXd3BS==LI{*p zn(3QoskXVORKpB`ti(ZUzGr~1g9(29F9VTyM5a>MR(1LBhg+4;ElIRx1V!vF5FF2@ zhM6Ym3Q}WLB*leK(=^S*zCb6Rr>BqC`~B1_BuAyB02^jk2&>A?>*MX=*4!!&$b+AU zr<>no4A?w1ZuCx!eC%*(=x4Z$7Fxy2%$8DnRhfO&yRBl=Qj8@pZDR%Hr9t}nG~vAJ zRTT(jcDRKO30Vg8LQHwtxly1+fjO+_r$=-@5GJLZ4MkR%u@3WJpT8^4GplDt{W|Ei z;1&UnXL4V(1e^u2FweVDV>;|YvGf4!xlDo`xW=d$Sm#QD3O?RoF5{8GF#4&0+AD>; zplIGXSNc9_kIi3vJCA~Sl7WyL`~Xk>my$8;P%saiVAF#(jSSlAfn5N%!^H)m z3xPW)CdhBa@0NL6cKYeR56KRF32e;C+Rdf`NiyMEl$uEkP*2_l9ZXe!17 zD+%yo!ld$1aj}wZL3$iQgTi$j^oRENO?_^rQs2>fZg;7pL8#7s@KoI4ALPlx6e4d^ zS(O~vD?D~nUUhkC3|=}2ZG9Q#I1`L^-5vSa7eQjp{Igmo6?Zg5EYPoA1^ z_0PJ4tOP7*L#Y`^P>NaTUv9q^D${bCk{C`C4#Oen_SCnSIM*Zs;;$_ET~)VKvHgIA z19`!iWNyiNeSlfI%Q$H#n9C;JoNsd=fWvTN&Wq~X1C)(PfX5{a`xE*b>372!Q=v1Y zcT4iD>mPppr_&*4Z|L%dCm>SzV8M@dzIsbD>vsE_i76cJ}U(};A zLRVFd=6TgX*piae5X&t72$t2eUpUqq+-P}Ah9mw=S2C1mqf#Sj-g&t{Po}e^0&i}0 zzcKqUa7qOqWTFrt$y9(OkUB40${-_(mN$Zv7uQezB9k>vb&3 zG&Lp|`K`5BEcUL?6nxbcTX8db)ikcW=+6|bd?>|I@wmN#ZQHMzLqaZ7BkNsvYj|X| zl%Z#*-p|-+9nQGOr_{ zQ|okm1|}4NRa-ylE~!W`*8$t`fKC;3SK+3M!pX&zJ|SZnQ|m-5KY}_2AElkF(K7 zLT8d^Npa@fRK*>R?iVXo`|w-^&gVmJg;wF3pu#9V5yEppB3g?+jh}ard z;@8r$ibIdnLbJxy!R0a?adbnq>^+6sq#hlHs>vD=72V`W%Hut^uBs|uI5&or9P-d& zUqW4SPX@eqwV6lui62O?Z3`Jb6iUUMQTPv1iViM5j;z?7*ZjtSG2SJ<&wDuU zQPy|9DEvrvthdr@HZA0Z;IY*XeAfovO32YTKp`+E6c0afy2jF@Yim@wnJwofLbu@r z*|`I$Y<5cAllbsmW!W0rGMyg_F?1_aOwj*|8$Y94T@)ezn;&T)AiDoQL-Jp_6i+il z8ylm4D4PEqq$%DtrwxgOoA(Ip3*cVb=vqF$k6uhSo0?cl(T@m=rY@BFAYc$tXgb8* z-MGow*WhE0;|cNxc$H<~gnGP=i7>ZBK~*(1wSPeX<>!kJjGu>B*Yt<>gV>p0dM`)q zhgVr&jZ^p5->;hQhlfdg_RSs5o$c3a4HPpu{D1xZ3{F ze{jWr(zBQNYIaRKY}YFpGT#qPw%Bo+b*e-CEZ&^P#^E|IG}TsQ8UDIh$z+_nX9C)s za?yMytOa=)JWsl{;Y?LeL)~qgml*i_UpQp@o;u*{im27!eOFpceN~h3;*2?rTWUE3 zEz&tMZ6QYO>alaaxhuC@rfzs|*wV~sZkH9d!*hFXv;E{9mxA?9&4K28{NK1vjGm*q zY~R(V6flm9A=E(>H*6>Nc2t)g;h4}zoXqsPwTTi~o+Hd9E?PeA-1?vWM{R<}HGMNx zP4ASw*=zz-+Yb*#s@86r1sPLZFF%WP8Xg`$?QitybALYX-x$p8f4DIwpev7h$$mCL zClrmcZ<;?#0h8P;DRv47Bw_J^9PXOlrV{JDb2dwJX+r34%2G_iT9N>LekIyXV_!|j zP2c6~Lty_=K%hH|>n4MgwPJmUSTBAbCa%97pnpAPB-UOKb0R~a$4*d1esJ_eQ#VO; z*_m{6GcJ}c?tP$nALHcgp%PG!=FPD|pO_KvVQsM=zo;m4br8Guag{ zf0ahVDEWvZdvgfvfh+<1S2sjF!$rM5{XvZS1rP~x#FVB8p=e;AD@0MyR%wlp8Ux}G zmNo~SL>b}gW{D0EkipI--HH{8bNiR-f=a63bc-xr2DJCsUw}UKb!|yb=_mw7AxN4m z<5cnA+}aU=2!;p8d^@0IY>M*y6%QGh--V03a+5lQRb+fHQfl65>GrY2442SqC!ZCjtdnAOU_h2SX50dY*GI%eq&KW(wx!LSYD{9$0m@fLOT`SC4p}m!th_O zOf7*-ymR=ZWgYl^DfjJ|WQ(TvCvPaEVbn*F<=Pj9cLDy4Rk0+BQp1l?TV= z1Ti3oBPktIW4@6sJL6R5VxMP*UY)TyHb{r(9xf`UoO+v#{nP6R`tpT_AzFZ`>ZoA^ zu>!Fl$yAlxwrlMsoNqY`gpW1-iI38K0Gbm&{7F>`%y>9J4d2-R`fx-p|K40@Gy}vV6?+>Y zrk1xm69MP^rZsP>DK|jG4~l-FZSNJv```ZWLHq&Y!n z+FD@fw6)_Pb^i95hqE{WSrzLD~~ck1&{ybbr0+_?$ZQ?&4fS>&|i3MCWM3 zwb7dl?V$+OXb~Z+WK-4iaHbA4@B?&u6O35Q zuBDfelL#b!D76vAgij%xA&VC$S%HePkn)s8-u205sKoXy)Ee+r%nhtRndu7{l zO1LrwGu`4h3D{&}6yhu*1*qq-S*vJol+YZli%dMj!K^7d{*pIGBrB1W3h+JbzpPNu zfY`{xqT7rEVNMcv0UE&mt9AspLKTAr+-ez-AM|kSo5Q{D2?qhPxTZzjnz2J?X>+Av zJ*J>`+eLrz`w&PFg4!!DVL(S;VAPorrgITED|qmZa)dRdgSMW9S0e)ng_iGHMV!WC za4Q7oTc>0rKNhYLtKl{6mYKQkL4p!BvhK~iG4Tm)&V0n7wc?~ZdMf&F#;k72PJSvn ze()TY3#gX_vXfDe^PlQFwr+o!#3Ua~;2xZ-MhY`X`Q>J&ko10k$H#U{J4W-r329jR zaeoe!#LA&Ul?X`|Rh7}|Zm=nK6)HV)0-e(Q0sf$9bN{B!O{(4)#Fd5|F((-3T=o*0 zm9*kp{~E@uAo56*Cn;nyMwjB`XLm8lKR7oIW79B(H@Bf1&!eCFmZSotAp}t&na|@$ zQ&Hm~TW3kMh(PrSFtxt*8zf2sTaXusFRuV4ZjMu_Fszi}bqI)EQXx%}8DUfydDlc| z-vI34$;qTS>X19Ej>rl&caHnreiAoa`BxT#*VeUGr1wc5MyTwiZN{M9teBDhX}?rE(q;qZjRVDb(I}! ztMzY?>&}YUEt_{T*`jJz-Tt$wI74wej*^Kw&-rz@irSxiOU6ogOaP)UycoT=?OJ@y zCFEWl4=iPqz~#vPAQOvEZ>#dJe2+|_{4k(F7b;7Nf=eu{ZbDiRe4|V{$CgN#a@q=D zkkd4)jY^RlY@Z_?7&Q=P&Z{8{or;1T2BDQ+{>Pv+$oGZWES1Dm4z9M<$_oWcW3SLu z-V(xty7;xJuhgim90*l_hO-r?-Ii0?7tuts$V$_oTf&A}eBI^ueBeX1h-(9g#1MS5 z&=__=7mLpY@k%+W#~4x2G?jP6{TKEh-BBvq-1pm0$Mi0safR3?M%2Q3 z0xlp?KJxR%a`InrCMY;v#v(uhF?cNzO2j^@pk+i&PYgtU zakuXz#rtW6$Qc-e)qnAVolR-!Itc1|qxhx%Imn)e+wRswoC6G$y7(wjxQKvXxU6{h zG+HkOHo;0Hdq5>1QLYwZQ9xw!SZjL8y=iI0iRpuz$K9en1q>94SUc7WR!9v(jd-26 zN{@?c>Rd!&?DX^~=l2K&Ma6gMHdLYSnTfA3lK7Bg#pcMfEjRA|EHF<*#K0)eBpU5@ z>#}Ne54vh2$Y@cNp*c%g82`vHbhk{7xa2nrJDK${49l?v-rrLZMSO}h0%7mezBvjPWFatOa)<)}HBJPgD=qQLnAxn{TH z2e24j9R<=97H6x&YZ{>oVFZ-K4QO7u)DnKF8u<)!Ll!8~7I3Q3XqQ>6Ap@iUN=mK< zRfngb|EGpT4^z4|4UxSSnqAHEV;k-$!pGc2et-Og_sC%6Jd;9@m;|<4FVtIUHshVM z7$GFQ7%+6?AFhvB?u-jbPnN}tU(e`A`2@t)=6z;}N8khh{Qz}l=9 z98;_K01~jY_Ut%qN;Wx(@&^c)kKwLD&5$&M4a<#4&y%NT6i+K zNmp5~J0hOJ@uklS1~IIvCa^kweoMI&91UBa{R93bfRSyPhT6%ho$#uGp1J-pu@~D7 zhTpkC^D5}6achWp(X%1`0D2h{X7wtFAGZkOEAzuug+s>6N$E%0-bG689)mK|SCYF2 zQ=Csy$mh3Ho`KfPt|{O1F*VAW6vn}veD;yvA6SCGMyEgS0K$f3Iz))<;?fB{n6`2v zR3SHYnk}lqW6)`5XnG7CsYLNuWINgl8zzR-(#={Wb@-l(grFI6>K?abZ_>?!z^F2) z$kt{EqoqRYtP7xY)D{zG!|wL<7t>H?NB_RH@i)%DO&>09_H5>}t9oI)$HC~p17ue6 z+qEGWU2@QCBv*7yy|;qvr&h$;>%GBz-^PLCq;+y7gm8-a3s8KD6tnve+tb1P=!oAMjiBV1)CteO{$I1_Z^*r*Sn3DkXXSU>?gdZlfjDJrV=5>Qq%N*yZ3$>yq)WD!gNzhiVw zIuutC9rkFOyx{4?ad7OK@&z2T7LMs*&h&S2NX%1VdNToboX2TTX4hSO~koEqc->+B$k7gdi za=E0t;>{Az7JpZjZIkN^rtkAa;K82vj&x*QfS|DXssceo5NOH{88>gYB4)ACoqtpU zbSp+W&C{wBZpwI*zG@ z7{JtpRrDZ$Ep&mRNF47dxRSOB4x?QI*#g;&h}8&^#n;9<@OB(>DhRR!;%RMlpia zm{O=>sB#t!yzGD622bZAEdC+H@bBNm{ewC!d$A0ht5WuG_vF!QoZ1(U_p;3%({75E zapj_dHE{K47p+%EHtlX3&X4_pcN5H4V1!5(>rhIlfY@7Duv8gYZs4)L7-TvzZpsq` z%$!L+=lFuJuc6u=-80HocK;fyn}DhAcT3R=RNvrb48I-5QU>WI<$i zj77LxQ(Kw>sXsj^%Vol#2f9xOTZn>thjs2#(l5s}<3-~e0dpX4P|0M6bB2*>!H3v1 z7>v`27udCpUz!Gf9LV-nq%ZP|bZ+xQ8#EC5jl5&89h$2jCO=R}?xy4KB`>@TD@s2f zl(Fc7{%houbjd=TN1a{iGk0azX8LsIRaJU+y?Z;zDY|a2-YC3yHs!@$6wj(7Mbdfg z`7Y(DzBD5vH0qhZyPx5a1{|H+gD<@a(mrAsv8S#;=exHc2S3W5Zm&X6L;<(*cFU2CM)vjHxtdeQKCMV_s;Q^rl|V1b5-I zSn@!pHhq!*`n4J3ZRav%awDN&e>6h(ZF$LG9+k7-^~i1%5g|cmWOzXJRugglr@a)Z zwNS^jr@4%TYK8@|b8P6w<^5g8q@x5f*17fiOG8gK;IQNuPktv*zn^7Ca)bp69A51I zr(Dc_vW{uq3JL8BaP`+>V6of`_!mT2T)ptD@=v*_2 zDzc9tmkTT1b8|+yp;*N6fz5KBLQv6%3Y>mNe@w{npT|F)LP(4?;84)_mjqk`D%I80 z%+&aE$S7{~e?}f!j>^(jh8}_)ne4|UHECNCw?z5dsdM~!eD0PSOlq_?=_V#B$lsoa zChT+lKlV3L$IevaJ890Xa-t$LjkM^@6u2xSe2!IKf4=q09Ghb4Zo2^H*Q%6D8lvvB`gV5R5jW4k*$IQFf6lA#1#=Hap27rS%=1Z542)$pe6|R8{ZMl5j}{ zc8({c{Z<6a9o?c4tiG%jm$1I|7Uny>^p|w(^}c*6dan9=>`OcsP@U7DeVmklGGcd# zZG}jwQ$_-`7I3^eGW6#PVS8&*oRZr!aYTcZM>i!Dzt_++s*z5sIKAk1! znZ)AEo*J9*kM0^frcC3RMmG3eT52mpK7>*ayfwhn+DRu~f|!avR@+AppY!BNCmfIJ^QArsw8}i6hQHky%u@eAULOIRC(S7xVomb$|}VJXo#K*ajTxeHLR|p{RzMr z%yF!Lc7}-~N|3zMNaW zPi{MI49}PL9V5>p8^P)$9V0#KkT-ddkZW_H%4^Lp%C4+DUjAMVE*=F0-8%w4UH+~A zR4QUAKxhHD-*M{LHg2aY%|7VI$1a$-;m8-axBKHeXwqw|zfVwCEsN9Z+}pR^&tqxf z%5-7k*GtDpN2xgaW*?Ev!G-U?s0yD4aPTaH$Fyyq1KcCa>73u~?bO^xi+GO-MNhZ4 zx5H~g`tv|8iy+yCwb%Vb-7z?SkL^4G)H z-S^lx6R$@P%{uS)k3N9`#HCI4XJe!Qu1*kSjfd;qp_3v->E|0t8J~At2JGBIi0ctP3JaI&c%@2c)}Rfu)ZX!J7?2M~tt_2ZH>k5%S%|3LjWfn13UT>=B`U>0K!JfNZWXi?noL zL4W7Q+*{!4=F7+7^?+R#7B0ZhJcK_H7R@gb@t#V)@(gkmv$+}(HDFO~Kg4f(6YKf& zJ;ge5+=ILqDgTF!u4jLzz6?p^l4FAHK6r&Jr^Ei??dVq;xaOKM+~3y`9w)G)6$(z- zRoQNp9S>4uo1}|jz0u#@E(cKPgUwwh4I@}rHoV|^iYr`gAV@}?FV|1;$XhmZr+Y3+ zRVfbiLAQT&ORnNO1B2ngDuO-N5G#V^J;`!~a?xpeg#bg=u&?k&mzLsSD)xR$Yi za*ymwASK^4VgKoX5$xJe&ck^C==&bc=Y;(_8R2W$ysGn4b7w$zD*7Nl!R}C+dMW=- z(Z!VNo(s*~d>h`x9N-S(0epCF-W=|@NV8gO)Hw#thY$0KC0%90q!EC#na%qVeE1B& z)$L=?q%!R--7upp9cUpGQ1%8b+{Y}Km7aHX+?LPz;Ym8C%aV7zJdybu$eZdK?qbMK zjd>k_3i4&!@Jqo=Yx2!h;B7$e-f2UV2wf=g@fnbIfd?eQp-=zNVFCdauXDs9nn02? zaZFuKJ~pu^NLd2pug4}NFs+y6U3ezYyoChwG|Q@X!mdh}m0HvNeh!(>iIZ`oKeuy8 znj6aikSCb1l6ueiIzlEFASV`{fqGqDPVr2keL(II*|JV?mmm#ftQ6!Ic@oK1seO}P z6=x)vQiUEAMlud+>;n4rtCVLRvxsh^$PP@3N9{-wMxh4i>NW zlMWiflA*t50q6l|gbkBk(@F%-#<$>V{5Bz~O8{)m+2p-+S3K zM>gzr0)(E(%dZXyQV^i`<(xDp14{?avJg#!96|lHs!EIng}VG1u7O620>VL!q95}{ zq4zYCfAe9JTRI39m4@ZE9J&0e4UiUQ1sgWaLgkbIDnfnQf!8kzAoKFCF>CPgY@riV z2Z=5C%Q1_?%<^DBK$RgQ53X6DIAIllcyHdB2SPp$ps>5say;HhkR}K7U#z| z2P9fSEHSWoiY#06w&-Q@zjp=84RQPM69&a?O{FsrdV=w`DtLBX5x9YdHfju~DqvFd z(GxKXuFR^7U|-)N{)yMzdXOW?BU;fI*34jeI`ZK;CCG+Q{GztO2nqM(ttD#D>;te09a>6pc&v8q- zY8>_L&+k6jMLf8C7Gq+@qO-`oo-eG^;!U1APkS6=E|u9=Py2?w^Nm397@4JC!?&e3 z0@kn>xAe?FO9LZM{{68_aF(se{%@6vjmFv=G3i7!g=7El+eW3QC<=MNC?xMKUKZn$)Y zltor8SsBjv%qX*P=}ajSoRWc&Z-MVe9a^6f-A6^@fo6sc8D;F4%5s4k(&o~+ZSUS) zPB*oKb08WPct+NP_;_ftVj)IrLjNJJnE3hAU;%}DV^xBh;l-O-E2|+V^dd%WE`s3M zDw5H9`ef)e>1{qS&)?hRDCSa_A&4t>?z=z%bxR|cW07(KE~#1)xO&c@?>`GG10H_kKvO;vVbI zJ1)oH8T9-1R-0KYGle8BQSv1hsU>Ucc!CRMzpAQAzakm^Do~-GJ_{@G&-v|Alv*N# z`U9-VGgbk;+{`TTpitowC8MPjZB-pU8}*;wNVIM4ItamJl3VFxZ8NQRj9U4KpvSe1 zG1Udy^DBYxyLy{yH4Kl52XTO)@{InE#xfhOivQ<@JA+;xKTU7Bo&nt|{?8{gZOsDrIK_t0PzQtL|+Vz{?zC5pcGC6xljsG=VhtF=?x-~5Y7Fv0JG@A!h8GM zb)h*?fO2XMtZ2)AGRdY;7l=)W@^T*K!L|Zs`h5B1ig{~J*SFwaK$0`WHUJ?3F|IUs zT-+nNx<)<;U5?R1C2Z_GCWt&8E#yaH@=lBRP`$OAx~XuYW7l$Dp~~#KWQvAtrOk?w z&Z?NR?*$>JKYFTu6FWa%y%NCN;kKCw8xm5ok}3>&7}H<@X03H(T>mv`p=@3@jP@j~ znV=s0O7VIz^&8&8u`~wJW8)(9Vj<~a&x#4+e5FIae9C1*EAZ)(8@^2Lr!P34e$qfc zYBmYy1i>AP`d=S80``)}U-gwW6U{G*I`%<{R&a*WC2)s?c&E{b1>kU85f~W)0@$~8 z^pZ|;o+^7RM%6;=tlx)W`Oc+9 zIwjj&C1Z3#VkCu~JK=|nabXF9*#R?PUkMMP(HqdAYNK`xpXv~Kyq85%%0(^W^I?R< zR)FHSxTce!?`id4Z;V5c81KovU zZ`#t{{xDR$72xV7ZV6Gp8$+|<31HQe$y8}KDl}utsHh^$?c2P@N-b!(iZu`fl)$*4 zmwFEp8cZ#6N!}hV28?H{H%E)y+*QeJnr(&Tis`SkVG_z~k5xbV^q)NKP&nNx;99U= z{W`T|%ftuLWkCFemwzKjs*vKiE#_NNjW^=3xm`yI#XClw+YbTsC`QCAZ*gqI8BLJj znk=!DSw!LlQQ#_j8D&rLG9=6mq**prDz?ccfmL8LaV{Vo6L~UPoNqqij~-5qpkP&x zO44O`FD-;FXp9L1)eN^D^i$X#5YjzFvw~X%YzG6JSY2W9fnC_Ksb|^t6ni-1WX_2`)So5*+kHWv(x0n`nJZqItw{3 zv4CFl#?iY#vxRC1vKrO(p3G=T@W`U40ICQ>)Ueh340B22DrDwp)$$g|m4mWU9xk37 zwv;Xc<=Mb%j^QVMr(&s(uo^~^XTu!E}kOCTOmIKmL zJhPeR+ycWetOesXkJL)Ux0dJ)kZa7|?$^sLbE?aA*wh(y$0s*J^7N{J1v?m+?cNG- z{-lJ3Cr{3*%mAZyZ^PoktSTyeTESs5@5Dq;dM?13=;&0^XOBOc=#s@*<;5<`^#4hhoM%%rQdOh2@#(ezr@RlJpyGo|Ws0Ts+2sEUwZR$;EbvXgyi?cmmA z=3zS;nb?dKcw@O0mrz|Fn>0 zd+r=3Y_!zgw)U+e3Tx`M4yP7?Z#TZ+ zp+`$>N5zV5W6gHj^U`pgl>zSszC@M>qRy1_&tePpC{gB{`Do0u- zk`x7fA|S_AU4>ioKX;~i*Ebhjbn`491K)rU3R#Nvo|LGU4`>3=7VJHny#C&S@{q~& z!nVD79oXCJ+lyW6Gc2)TEW1jiV2v>o)%>%C%7FjgUH^L%y~%<`Y3A1hn)Nnv*>C>S zCDOO4CcvBQ+g3fGTE{34V5-Of;L0%P?)6KY)Ag$ewW%u>IxvlBm;Ib-IDCtKe3|!| z2%H$8rf`SuR2nA%D!Q~kV-CJKPa22%*P|xJ$8D1#!=r3A-}MplLaq_;05Y}L%;nbW zfU!kr`iT4`BgG#9yU{tf(O``^@JE&5QQjZXew*TUCP^@F! zcIkGJ=|;KC_j}~l$*`%*K6KQ*m>%^_X5}#@T=w4P68OupQ>#VNH)f~6d2)KxC9l@} z9Z%f>-*vG_c(96!plQX$XRoc~!9v%5jZ)MtCAV>IgMG_lnI; zafad|4DzbU&j>5Vx{Tnec<1QB)^z}tjKS)X(y*I;iSPSD>V8ZlH8x5sgGH-@lb_f<-$$^MM#G0E(Xq)&X9|7tYg8CVMc2%VeL zIf2FcLLuzmzfoPnhomaQZ~wixQ~T{8$W&bl1aDAqvj9A3k3VylIRU+BVk4KDu`;Ba zts^w`$JT?9u-EE808T)$zp*Hm9K1sMYjYqpGLM+Mll}=P6b!K$T@Bsi5__hGUaJPi z+**!)amL__<0;UK+Z=5)6IOR6SEXx2F;SfgN7%u8(MSZZ8t1?qY_;r!A}Ho}l%Zht zCKpvdx_6YeWO6r@ej2MQ!vQLU7Ehuz!Z?DeV)&vBr`7FrF%NkC0jwk5i*OtZihpuu z$d`-a=L5x2yQO8z=NpI)yJO(D@oE|!wb+>1>??Jz|C@sl0V*0t4hq;&Eja{+X5ZP> zgW*`yncozv4?Dmm)hebVw7e`X;S4WKkR7)DQe*xcp+L;An84^FEjV;xHZN|K)1z9Y za<09{r)1=*qEIFObVEm-!viYdF)XJfi~ZAOZ)2-v+@4H>F3oX+(hQuyoDnC%4(HJH zotk>`P(>puWr`s|)@k5N<+d-w1V!3sJLj>!`0L#a*@4q@ZSq}(Z-PiW4#3DNtq0g^ z4OJYADjf43zY+Fu+T5bWO3ieY=f`NYsfoJ9!7I{f%aOIs1((3fh{r+|>{>T)rH6k+ zx1B@aK$#~A*Zi=#XUUs`|9H2*ZM?a9mL&q;Zi?W3`bkO@JLVQOD4j zWwk`#&*WpiC{_F{_NpqNSWaj4qTU$8&Tf>oXMOhK{CvYXw%l#^6q`#W7{cb$Od+v4 z;iB5`5deGTTnEddP$D88u^Rz6B9E<)O%?=eXki#*dk=%)c)gGL=XZt@VS;BBQMvNo z2c~Q_M{7$~NF?_+e^@>YrP=Tm;NP@R&K=6VTt~vWQ6XHW)kp;?(vDB4B15022$@%Q zz~(s6fy|JN0nF^_sPe;_2$pUWoAXVCTSq)Z4O=#c4&c~H-=f3mz~;C&O{j+tGg8%=?^o2vF!0=&mE;P-B+B(BnnBS)S-AGdI zTW#*U?Q)k#OP;)_S^I~&AeT2(lv5O4LNgA&hoN}Ub%@VV6F-l`lI0h5HC;7aN$w1E znqyCFo`dA%x?H#r*<9EF945OMGqc8W%zj?3ruov@Zdkil3@?YtAIHVbAM0jZt(raz z2Sb0LMLwSA)PKB*xT#8EZH(CmfSC~RV9yqcuK4(hR0vAM51^76hGuuqodsB=AFZIS5+Nb=4KJhP4BS6`Fxe1Do_= z+?drtdQ#G$+4Az#{f!Lqu_J%lUWTrjB)eFc=sIyA9?I>~%Q5VY~bf z3R^v-par|9r^g4cKAd9sg7>?}C;Qp^ql3d!j8yt!J>+GVJJaU z^|)p~ARrhMWs-GyAA3DPI>ijjJbIpOaN@zK)SGx`thP6BPFL&N7sU3KPJY5E#DKBT znPY^*00?iZ@0NCf7?IBb?)x*>3*o1ulT*hRg3oKs7W;m{USu8@x)!k_ehzLFD#tHk z?t^jLz_=E*>(?QPs|tGoBKBF-yF)iQHS21|W;Ccy%^_;BW1*&^l$sW!lngzGIMd-0 zsrP(<&_ea5u_$Xy%YQEJT8KG*FjjFnUxgc0g6(@u|h?1&Jx118vJ;mRSED753zVYAcHO5ZgXU6 z4Kf<*yrr)_)kAT)_zdI@65N5mQ8_wlw}D=lwG+2Zfpr+{%xA8( zZpKX!W;Ib;_X^CzmX(~TsZ{h=*^aH|tP2Vp<1j-3ECH$d=t^cw7tOZh#iM!re2Zl{ z;#{xjM{poNcV~o3VP2k)0y8 z>#5RG?^y=`3QkTwUghA^}_Mvbc$$xK}Xk#?KfHAOxCs} zAYdCNtLapk{*s|P^0cXqC#Jy-?nFFLK27))TEZ3z<%TS06f^CBoJnLk=av>rAdRT zLcm%J*j}3|O>L=*)n#u=T!23>?E_tLys-@k+c0EP$Pg3_FGM~bWE)4*szwjy2H5>j zvgZ6x*OwT?G7-s@)N+LtRF6myfbYD9IwB>Xi8VrV` zkEMa>RwP2|#Dx=IHJbWwaPE`W_fpa@E zd#~-VmHW0!0Cj-zm!Y*4=(!OW@DnPtEeI6`d<{3#Z`XB8zj;N0P@ats2I+%odU$a7 z=4ePEdE5cUg$^KgJm`BL1QWVJ9CViq;vPAX&W%kD&}a{BiqOyN=vbs0eN=DVqfBcq zeeg~J|0TXE5pl*XQ4`j%Vevs?&fjES+@K9tQOpfffeh`ex+x?-1)_Q}DL)O<&ZThs z8IPbB&PllKJC`o51nXW;8Y&IacCm%=X&+O#?oj~gcv&qXb7v!&sjjjh9);1taK&|Z z@$LAese+kun9JX5o+jyDMQMYUNl|h{77nZ}r%u^xi5a87@pS*BW4CaCSABMyD`2R) z_3u03C~ZeunctS_r^|@8jT>M&@A5U6z9DzjC1FKZaf>&1)%aM?f{~8d&uj(f&N8#! zLK0)TttX$DJe@I2j&qG&6s)2!oKR&nnW4o4BHfo$B#Mmy#&%SbdL981AZA?Cq|vVUZW` zzLrH?0-xKUL8(YMnaayz*QhV9#a5SStRk(^+JU>K)Fx``elQu(JC#_Gh!A$CnpEt) zRH=Ds?$SY+$ejYFv(jv!)7+@!VLFxA*}2evMN;o-Js7U-PC8aPEwW=mb)43Y{pnjz zlNQMd9H`X%N?|Hq0u{?HRJoBC(;qJoPbN>hHE}Dw@F$H3(s(?_H6jAFpP$VtUO*h+LB>wUdf!0NQK*Tq=Fc=GhdA+;0Msq^6?N4v}?oVO;w~%`;KX zTyKhU=9w&KGU1t0YC}19n8Ly@6fP_bZR8qFsi)oZjTxWBE&rWp-l6{MFwH$GFzl;y zF=Xsg0-F+Rj$#WtVM>J!O@2|635^@Ll|aWMoZ1fPrx@DxQ4lG*EC(16;GP)^BfH>i z`#?z@$$V*^JYUU~B`^qeixJT9JJ?5fWh;m4W(iWzT2t{MKX@k}&ku)> ziDB9ydzIySW7UqD*5C@Qnqp}I&8l!Ft#03Hw9A&HoNqe2?B{#N92jZ(z@bbV+*dds zLz4zXx(9-Q4>b85NGkf+2Jmni*clmwk5jPL05tvi-VPMb^_s?$x3pVS*U` zPH}=OK3bmOX1v6k>+W#_X~U)G%CuF*?@<@9&W)UU6Fmu9cE3`y8dolD9id_A7>de3 z;=7GDzw8tdBTCU7+yHiU7e6ZaH6>zTb^mVNykAoA+o14*pTo4+LJ@mE>>yiGo9K`r zj1BV<-j(zekg%GabX5e+%7Dj=Y8_*)SG#JS=~f5UI{8M!RMYh=vEE9Ux4XbgXpWRU z1!0}~huR9f8@k10J=5wIwLLR-3&qof9>Gze;HkjW9DZ9hRD?45{+h*hl+>mc~sePpDR7pVa4ISrOa55kUjz&z6y0s+u?Btt{b{@9mLc$Cigl!J9^Pu z>PW|-4BU@bQF?BYdzunTOHH;lK!+!2=BzRl=!|#c75V-Whr7yos362#&DOZdA9=)^I}Mv&bm&#|H{?Bnz=dK5A*2lC|3hIu zl&~zq83zKn?gec8IhKfZE zOAXD>n+Q6oX~4QqTn$5>p`phu0S^e978B`T|3xOY%lpioDDAhWG$l%5rT4wt8w{dH zN+}htAjmf5c?No$BwvoMJUv`g?-sPX;j#@x8z{7!ALqZszk(eAX*UR%m~i?d3! zy=#?obqf{r+u1SN`y}sn@XyyHOdRYw>{;7vocxJJ%G+0H?J9i{BIxv`iwxqYl@#V7 z(3Tm+e$r&FpS-oy6F)5q9bH2Q)5&X!dax|>aXB*T9Ex=%n7-dwzw@V;pYH(EvZr%P zxjRU*Sd{KvycS2EPMTZSkb2az(Bf)eth5>2e01`{#i5mz+qR zm}SezAu&k2ZPnIGIg3izwp=wIBThdtiTlkU@yVInc!vnm!76KY3Yzxgt*XvAo_5MK zVm5Zf_W`rIJMNN8V=LGPlq=24p~yCb_*Mqm#z+8E z@HurxL=uC4Z&1mlsPl5k@pte-fe7ptyM)TQApv=7m>A!Bl|Qnsc*aVYwEP-D-d4%i zK=equ{yLc!H^o#S-y1wLs26}D77KbZMkW;Vq6xhO{DXQmVX>38<{xW2V3{kl4l z!P4A_e5tnTOt+2tz&!Hkm`)p{v*L;>{u{rh30D)^On4@bCUI-0>;issM!+7j`$ zgG6;A9As1%?4YAI2*j-;NV9f*40k0yAdYuCu{&>ID@=6YSMU0C`@yd2V4pM)LN0rUGUb`)hTM{><+94+eJHcFecd z?BSlHv*`%sVP9PY7z2+CR*aT~&Ed-Yip2_2Ga})SHF%*pxQ`6iCI~ z5ixb18Nwa-(^4oSy$pjy++}cI9RyLJzuD0Ze`D+Va(#C`OdGXF_be7dM7NfzYuA3n zpH7U-aSU~)2j|j9E_I7-sZO;qz3~rvN&a;V0LBQ1l-w@i=zd4f*AWwfF}6wC1r(dF zT?1J|L}JeAYWIGIi@J)&Ru_#rn0n61Fo+R52Orm_a@9wR1bcq&SXjiJMH%v&__TH|Lyq6V|@%)ih$X+FK2}t zuOtif=1!BHbJU9StEC4~Yqq8im&I9oFtL}boqWRy~yjDf&28{U0f!NlZbvT~cxTSDfVC^b;O#z;{^ z)7-TYiwq*|Sj@%Ef8)RmvE%;huiZ%*Bwv5+6?OfsATHAJRDudRpNTsR<2%<4&FfkdFkRG7Cx|*}A z8w#KW?5na`Er(rq#ha*DF*pHnBw;LS=B-foB&4CkM zwQzc-zy0Xak_+D+N2@(9V8}Jbrc@3ID5wgac{{mRjXtz(qDbFeR@=MV?TGB%%A)%W zo^i6&tel1M#2YAqZCm#O8Z+PzTee9gx<;!#46k?t2{;mC`Nriu)(wB?I}Yd%()ME9 z>8c=lhRyC^d>ED4BGo$iw&=*1r1Y-0xEZL1`am$Ba3>(<_TCKY!HJQ)xZ$YC_uSBf znQniAt#qcN!CT%sW>kTEZ>C}qTIl>D#NC1@WKoOl8I?>rIYxrh?Uq*TI$VeyhA$Uf~{O@S+ib>T!}Te z3TxF%50CsH9qLx8-MQE>%#kZ8^X_Zt7&P2t1{P!OMlO#Y!3YbzK|ef#_^Jroj1kbn zbH0u02E0pmDtI}qF7heq!7;A(e-?8sH`;RYlvzvONq`U`!g1-+zt7pM1{p|4*)A57 z&IadTQt0>x9e~Rq=nXK8sR0Gwo9eQzBpbJAUH8TcOrGps59&Fcbu(oo|5%Dh}vjvx3xI?GF72eNX7c+5BuxyH+>q!(j zXHt`GMI}$&Jvk*Dc3So-j%MR*RAx$w>iK0Y5=N+dx58JVY4-`_QQU2Lq;=QqR4R8@ zrHvz0#L+#p+5J`Ghr^a|WvGzNpkWB_PkE@sJ{_JE&6$u@j#ty-*c9FrJm;v8fH?z& z0KB1++|@`&RnMMIGV8wKa1kF8*6y#c4 zC)JG?D-qscU~G8{Y5J({4=^SVu%m+{!lJ}(DAzH00b2vT+R4%OL*Uzv_`Vh*@CD0E ziR2nAtD|CX)`BA}+@Zg|wejgyRTpHDLavTUO(~JlR0GX_N;`{`q%&_Qz#%8!^dJ>C zAicoy;n^oNI_PJKQvy873<&b1WaXMo-`SGI?HR}k|7?~o?`+@cXNdBl0`jvI zlY})&^NF`KH|AoDe`N}0aMkK>bnQtJz51YzFIV%3cQC#(bum!oR9SqgXW0m*&QBxq zxSKZELuGx%`lRs4cYUYVXz*BjhofnW>p^p~nQHF$Q_KBJRB|m?g1!(ALb(;%G`=RE zled|jLQgXA2+UZ5J!MY?jfguAS`NrYl6KWjbgxOl_O(fkM06H7=ek5^LGl6@^m2tM zlsRM$huPIh;i}3Bg8ukCA!;Ur^j5ZyzZ6>DMw>QGol76m0I4$cYmr|9KrHec{||_2 zXMQe(yCyT2yofKBm#Pw*URV{c4D`0awLu%(O-hpg-fZ_eo{?or8iYMTL+d`NvoOpq|KQwI>u zg2*sWtJ(Cx$*{h&mb^Qpan)$3IoFh{jP zaanvCvPXSSJsj2`Gy>O(vA<{eC;GMXvVZpcC9tinh9aF}zP@0Ub9b+r`@_;9Gh8HN z_hm<|tubMDe#g(y=k2`Xo){`z7pTS3HM!c)#T{o#=XkJs*!`HPX`AJD(9?m6oT-n1 zz@*h~o(!YxP-#?q_qdmV8BaVzVvw}bz{j-1Xt~I$?3r`Hq`j>qL;w-qRSL5IC2>W4fa2H>5_~yiqVR>Z9VvgXism-Q9-&v z2t3r5G+1%43_F_0UNgCRV-56!k2v!^n*SiBwI<#>NqUjhIf|nHS$NhzVNRfqesZYQ z-Q_Y@)FQZqsDKHnk>zdM31lRZ>(qfbG_?F>sxkDS{Zf$iFWyI}n2on8(Kz{nk5?m< zi08#^vc>jPTYU1xE;Z;dVEYZcc|jm_i=ldC42n4#Bn4HYV31)(>SxKcye^d-Tud6^AXPjhCEzev(t@B0 zbQmf{f#U$Yyzn~cE?1eP_xEN;JjgU2sw$OhVV9gx3KL_E@v5J=&A~dkx*QldH5tDg{6!|0e}^aLTmu3N2D_I6DbWDGqpf z#}P`t|Ldc}PfPb0w}YK=1v%WbML3Eboh1K>y@S8ly{!QaVe9?|3x;Ukvfw>;inQE70fGT)J)ACA z*$B?rqDK}52EWn&N2way5_Bugh$^KOBAtu&&p~tEj4IL>sB$Ldvc{wiH;U4B>X=@wE+r%SO3g7xDH<~&xR+D)fK1MP z&^JHJRtwe$-@_Qn48l1EKENA~bZ9qC$q7KhXwX8D7zZh&bsZ6Zw2ZVGNj~WDw;QC= zx+HI_z|V1JKqjDitDp+?2er5z6yOk;@&=B&fRvQG;><6-+M$1jWDMU6kVDtDLCSz# zwZn|1Mu3j#7+R;p5n58Ex_>FpNW{z+9SY0&%FCmxIEPou3Z}g?wcjM&<@9;0`8BY| zJX~xtxyNr0tYUu|k9Zb9OUb{_fUx0p+50LvKo3atp}l~W%M;)mtAX_jW*=eH-OjNN zr;{9G=DwKHC?!r_3xJ%Xja+lfrdF1q+CD;I4sjTG0;BgF$CH`f#dP!0@|Uya2$U@a!lykynXKui%LuAspB=JQ^_ZI*vTiG|UmhWbiV z-KWMhiw3fmLMkPoaTG1G8$^{$qSGUj zOiGoO#A%FsaOJAQl zv>ErIMZ!gxfkzbfrWqD8nre|zxHdzA(a^8gHEmDx1Z@9oE?fUDEz5msJ4{o>3+E=D^L0qS z6wHbvPm2b{Xz2Fw1Wpgix}g$wn{$Y0&V|m%F`dfrkPMGIB~)j9zPi0FSz@?Fp#oDu zVQ3Sy+%?iZGtVBln_3I~hJn5<=#U#GuU1WxPtY_A+9S7Yp9oWY^1AR~lE;Iha8R?B zja=MmAW#ZPcpjMajH^aMcPE?pwm6L3BZ5296ghHrBlj9TH_vQ}ZSS$pt z+3~VvCn2+9?sI>*EP$D7icd{1aBI~jB7dKI$f=AMa9s|o<_#)GB32Ce=`yPKfU&)b+XF=$iz#rDgL6gW@Br?J?rL^I$)+D#~CL|DJ6oD$gIBD9HD22n-+G0hD21yxr&;cPEebCAY z2-s)lh(Vc^-@h&v@6_vF3?m0l+n>$7h69Pj4wb98HvZ@Es;Zk{lS)JGd1N1Ug*^*{ zucapikfbq(-e(^x<^vr2AYfG&uyWe!(ez6h)iCb09AJ!N)9E^(CWDp zJX+Z(g;UT32+$%^k;rjjI&L?erfmEFHp-`|2%P-eY(u z>3~Ta2@M_lftR_EYokfG>_!{djy_@N6tQjw6e6piAT62N>m?@9&>GRrl!Ojpgu8c` z%~_iA3Q!Rysx=-TfTQ?a$LjZnjtp`cA~J;OOczto-LWAlRMB48_@Z_;+7CH8c1CIE z;HU*8I_!Ftq4!6iyXBMH=sDH#N?9=!+td?x2Jxddm>;oc#Cg@?r?~Eviz%+1Udw!u znA+Y+7D?t2rysMo)#W8+(G4<3#`X)z8jp>FVq4c-PTC_gYKmu~$1TI@8Cwr|ZQ>er zn8^pk{MxqA1w^elxTxxTN%cO9+lRm&4SwIl$Q>N@JI^+e*$2}bK?JS{Kkp6dq_ zT<L8)r$DUf2+2`1CvYTo76BJVUs)H7%E6h25Jl(1{!v_=5+HJ8k=2Ce7Ub5UeKuK_}7kJ{}7Jlj# zyUy*9`i@qcw1fCd?ZJMu!vEOW2wl2PxQ)SeopmuZW|%UJB=NlEy?Zl$yQjb+VwTrn?qp+Jdg0nO4!s#;VWCkbYV{6?R2vDV->)21LYjxy^tiy*Gn+ zfI~mI$4ClA-}n*!Ft4C={brgsO)=NEZY@3;Gnm!24%?YWOrDcc}!iNBT%=PyVVV5y9mt= z2Ocll7fhf?3Q4_xq?;1EZYwLaz)@-7M#1D~@iQaf0#DRz#+HR1d0Wr%gG&w+p73_( zT08s?=G)OBw+(JN#TmEMR@l}Sxmb6b=8=)dryD>sVx;7p_Xg||ZBDOu04I|Br;UUf zpih^1nCfvWSzlIX0JcYb}&iR*~JkOgSu!nYlJVi zIdZM0N;a=&M@z^>xuyGU@Jx`*e@|X?cmCcf|6rmThBvrW&;&P(*!gXel)?oP+Kb7q z_1F)IN+xN7YsW!Ng9+ysQ&a{oSw4L-Al0g9E?8b=Q_^^Xq6# ziURDUNgCVqmR?0RH8YGMedv;hVph9losE`NU9$;IRBgAm6;D#~>5Zl^eDyS)vQwrq zVAO#Ef=ye=M+GJ6@fyU)8^>4N?bSn|f>D?dlBr%}cjntcydvv{oM z;i6hVHI^0}O{g3Fit7faDZ_{#o*|!AlbM6eGuE0rO@kh;#i)Qb-&|u%Uc+Pk(|7B( z78U*-H`b%4k9+T7GWRm2v^;pVYB1W)iWnUHdSBHM+%*&bd*d&!`7tbMIV;Fgx0kNw zWkWt|30{{Kc%zYQ_fz-k)GEn1(JAjYCx~E+KFlp2Q(0YAw}CqOMy0=-x*?-V3%J)>X*ndpb zc~SN{wm-1?eqqhZkVgxw+ICW2&Vl%zVg2WdHw%Nsv8tQ0QNrK&YnXI+4S&CiEL8DH zEEMePj2p+lXNYBn#-8(QK<4eld7-LZaHV>9sy8Jmalx1Gb zv8g>}^N$QvoWlSU@lyC9>8>{&E{nw!hSTF}xNYf6(wo(65CbS!P5QAs zaQKNk1bU5xRiU41uH%|w{pmUpdGk_iP?aW-qOnm5!3#8^;v2hS99)U39tf?j>Wpw~ zabx+lOgG~t-F-2D_#{!jXM?Cai6N5KB^Dw5fl!{b1obea%0}s~O+WB} zI`7h<#tdPc`&bj7C5-;EM$PJOc6-wuUNnM#>RS%5N7hRH8z zeL}g!uE>THcGzu^j4O21)nPv|!Mg7ycs?#&{kSyTl4zF zp}j;XQHr`gM{b7m9Lk7PxVWy-TMDOFo|>ps)Wvj^>KnL7sHzs(S_OUj2;gkQAr!Td z586|L^|~OEjCj#Z1^97{cSo+5%rAvXV^k&98C?cbPDXdo84->Ou#AI?C{OLAY}}Pd znBpWvaqx~-2b+yFgb_0*lY;WonHE3CT;V~YeX9Iy)>Ks;L z?QkdQgOx~zN#%WO@~4ftR^Z;aYRfYy9tq9Z}Ac6m`4uhX@s**fZY zM2%%_EFqGcGN+w3GIyky&%)#-iuXuOu0;qX5$Okp`*m~pFqwCpn1X?I_!oF{LY?T; zOW>!~HuiK`GFyPeAqak{gD=L?2q?qc79~Wh%7?}OwiuF;<%|0stUp;o=LF>xoDQ5l zwE?J93uUd*CFI8vl{bN{x%@V5mk?bV%OMkygp&7p^`=m=%Vw0PL1xMoV+g|NJZ zMUlz1d?15mam5FG|DcRhlljeP^vC%Pl4Koql>_2=wb-~|dY3B8Acy+IS5{8nFfw@# zEsd^yd`X`rER7Q1s|+ZKorR4(!@d)NAQm`*Gk02gsh&;W40#8Nf%s?1kPbi0ga;pY zxq>Q8a=dAxx(Jjt9%rkAA6V;4B#o=NPMHkjj%4)x0Muan%5x|KDi@cW;5$nUvZ zf}O}ydhpD|hy?Yv>)RnbdOJ4*`>dDqN!9L? zzxj4~mYh(JSbH{FmYCpKhKfm;aZk!Qg%!Z3J>WsBi(J~# zJX0~-FR?MJj^o6)HHVK{u2d*G``(7G5n5Cmb0Qk&V@-$(8w$iV;RDjnfqY_`GyiI` z;DcoyMWH)YDod&8fbzHGMYG7V(U45gs9yiE0h)Aw{3$T+TRHMa_R*R4FWpJCRy(HT z#ECo;cA27u*`E;;{@D7)2{svP+ReRYvbbe48RyA2^5v7}-v_w?U)d@d%YCaZfJS)y z)ary`-JlBGg;d~P%}#j94D>2~0Er2;W7rYjQLvdphay@C+Cl`k-2m1SO0BP(Z1wI9 z6Q}DVRw{7=7`TW2RwQX49|Wq#BhCkt6-gZts-0{Tj%e-f=wQ3nRgIh>seg#1>v*z& zDT^~>ntZxrUvq+YZM3NB070mRWUw4WW3vcr6p(<9Zs>%L+U(=90m`xTfVU=T&t4Nn zt#k~Q1=q|jhS9YQO=C8#s_WI_N!ax`AGOUFa>GRw)66@*F!aXL%%URQ%Nc~%?2m~Ehre}C&ih*`1q|Lo=2Hwf zi#&BP4^?uXxfLGS=|}WbT$B1P@*8eAKH7tkqRww|efgY|1G47NJ-7${;p3sC%C|Qz zaMKp^5!%lHLa1>vtZ z#_b3lX_1JmZo?VUS?+D=ocA;5sL3A@2X*5Rr$lOU6ADAy$UjR?k6s^Xb>XZUucify z;7TS(E8*Y7to}xsrZ5L=K7NjhPAZI1S_%aa#aD{QqHmPUGB~s)h|2KfQ@fq|BrzRh z7bq_ty(3EU7YrqU`z4<9-hNyYBy}06IWPBN2NaHTvz>VH46IC>U~K6w2%8AT8w^LQ z8DXg$Fj#+WA9Q83eBX|6HY!3j8nNrXbU>pm8Ei$mSQnXMRHh`~9%~aR-;KP7_*z~3 zLqwD&^ko)7UEw+^9X|<%Fnx5Ga9-PioZ5jiRRhsmH&UzWlE^_|?D04jlkTOC$)sw8 zjdD9q|3#xg^k_K~0a5j)+S3T?9{2}_F%ncI;(~`ZIppmVV22Z25W)Jf2?;WaBz&LE zH+L(MnG)8gmmVIaHhiHw?Wz;)w~RB~2CDVs9*u;}#hzXud!~me_d3{vnZ8hpj3XMT zNtKgEVMi!y5{IjG(390PQGBk;Ger2{0L#N*u91b=Y*AFqNN+hVm%!Qd(qH#aT2~}C zSScy09r_3*;u9pl4G)ag`ktj~Td~hlZ>~K=T5EPu{k4@gq4upnHlfpPb=S+!Ei~QJ zEktO0H$jdXI0u_i2MKz?nIQ-nz4Q>UU#0M3HN9N8FVyz8Xwr3CaGW~M`7|aAx>#QV z?v&w0xJaw_G&&qlZDip84pEvpm&_!ePVsUat2_a62t+pV)F9Ady|5jf0W((raRH?5 zj41HZl}-f`0zj^}Ot6@4Kxc;whktJX&6di4`0xIC*9}WvD0_8Dl5sdWy#H6DpfkTG zrb5mIPNyjd^Jl)7^9W-2E)*i-rZZ4or>DR0nXozUCb#}Zgv8wDIxVsZAH9GUH<`jgy(O)yE#?k9dp{$n}ZhL-bv+yx8 zMQzJ9=&t+Av+Wn8>|x@iY1bTu?P)f;%jYJe9!wnC&Y(T&8nZU_5!P)3tpYxqY^C?b zL%PKl5XjG%VW0Q=p*$0Hk#vVJg!P7n@4e{gd>}HDUqW0fYbdKSL5r}RpRKRO&(ldy zAaPqUdtFuEEp!tqwhrccVbh+bZ}ee}*{BRbaKqWFB8YK(-J>}4TI)4p=ohi2M4$Y; z?`r*}x~N`ceh`t@m5mFCq2io&QayR;wmnDP5`Nz7?I67?+f}=@bjr=`r|i^6p);ODd$Cy9al1yhvhBIT!r4~Pgfrhs34S$+>9d81;>w}ZsS8w-^v!8z%7B?u<^-M(O@6kt97!w`to*sPMH>o=C6Xfr} zj|iiK;s}glkF#MFON6 z@q{Je7*fd56g?)SP$wbpB!FRt6zIeIb~C!q=Ct({i+YbSnyw11QaK}Dxz zu5TyE+3Z}U`LFHPU2g#vPPa6lGdJfHgFz)+*H{Sq33eHajLMe#F2Q3ApkdP05@RO_ zV+eAxC>9x<81n{gytNn{oD8Pgotpq@gJZz0`}OPn_xp#h_Ye1e1-gCmc|^xTehSI) z5-rJSX0Hukq80Y1w6Q+s*iA{AVjlQnwrK8%xTC2LnNgXt^b2|YIon)`jZ6}QlW7g( zDN=>bfQoWjW1$ogf^sK*fg~Q|$hK$IY1^qBX~y5J8)t?x;3x3bPNL>S>z{y76-!~? z<+CgrB73K4o+z^2#ZWlqyAdTc2wT~fH3E=Mf+7duzmu7+{>5j7GJvKTk{J_JCEaCZ zKod*)EFfUe%AP8^4IFXbxC~lhF@V@_)6u<_o7uNjLztCamkVj36>==7`3BpS&aZ=8C`b0Pj2kZq5Y-nL}kgcD8L0(nux>2>blg@N= zy&gskLd}5*hk#~JcqPZ=C{KWF*CpES>A?9a|18R#wwHmYJBFSbV?s>2xt{x{x$=%~ z@rP!`hr4513`CvGIA=jEFEy3x7#wPvVCM;H9n~=@rOepq_LVxltGAEr>9b^?qhpZE zeYIS2a_Z4^H7+P_q3n!aP4%8aBEpLP0Sz9P1q={~r`sEqacrlhs#6vy&nidrt}$&U zS+W->s}x{pnya-Kx3DYoB;8YfLvmzz-0AEn4|qLr&!8TLC=_8nNAhv52w#gDo4e;` zW0C8|s^jc$r>>dgLGNqbkZ2Co{?6EEy4BItsP0%4=J zms){&byAF~`S@*dQ%qS_zc#+{SM8pRGk zg`zB}@hSM-pF2`Idc~i*gA4$@f!-axZr?y$`iXn!3v8p-o%D`67HlMb`~~)rg5R=@ zK7)`63idf0M}c|D%6!(Etlu@xfsp(&MxD&ljdz$HZu{2qm*^k)(qEp)b(miCJrV>? z2{&TT4nT8^X)SE^%ae zt=`nkJoiYgd-S@-U+KT)OR>uEYvWy}l_^ap!llZ}(ch2QZa_YN)~AtZO-T|jCz(1DXFzv5I;eF(O}37 z&2QA>eLuTlk5k;FZN)Wwp{DiW+TF4%dVPM=uAtJ{w#VeIYkLF!ud}&L$JliW>RJ@Z zdz52@^!%oxz`FRmZrtFzl?oVD*3T*Z1x1M>lEK+p1iXu)xkZO@*h!>-6|})CyV0uX z(?FanMuFas5Peqx)Mlx2GpeDnOL5DvcbXj>zW;D4j8x1`G8vWIqVGPuJw3>dKAdv# zff)QAhi=3v!};1fdiQSk5E*7OlgeP7B8KB_7klJi6nwF0Kv}a8XOUA?-Mb!Xvr1tW zCg3a{$;Oxn46bRAckkoaySNt))+)w4k6XZ)%ex?nd%V253sGJzfxXNRANNk+EdB8Z zX(@AXcyhY?_HAg4^m6!T*QQ37e0>lxU1}{2?LDG2h*SG(iYvZh%^kVI=nXH(Ickj7 zai&7aX*Sx&QGE(KL@;p@H5_y6@a{G4XwniQTbngHPB>#bfKh zD}_RQElM#CRT79vxr75Qcd}6a>Dx!4nEqhWRbH7EQ>HUJ7?Cq($IIX&=0QPh4~-=d zg4{cG4X0q-bbgbGV!X_n%Vwahfd>f()9akS?;bUOVgH~L(k5R;B!{Oxi78 zaq7ZR0n^YF==)vHC#38ED#Txem9mHmq1PyrBYcInMO9|aGC1W zJv5E7H{kFH#BVf`vW7xj63?wIg}htBFhgv}HSWgAH=al7S^E6B4$14=prmHPf;T@9 z@A%?}@CCDp;Ttoh?qe~(?h9|S{g-m)F&?49Bq5z5ah*;b+X8wbju7=kdk*tjVbWq| zpfmX%I#Q0V?|L3I_Occh6Z$!f$S@v`U)o#F(sxD_@%)?vqqyc127+ZQ*l+VJS|%J2 zteR-ti7?RK9&#V++M_a#S)ZO~?aC>~@5*cO@Qs=nZzo%#>J}#OxUH6BLv>Bu;2PvA zinXfCZ(*Ms;KGM8eMy6yOvuN%;)TDx@C0@W2(}>dLPj8V-U24Z;4rSMpp;otkvDT4 zZ2duJA0vQu&J^D4+SS5?qh#?rZSD{eT(}4aslvt5=c1?kVajY%*ZB2 zVI=b4*4EY@ne?zR|9D&~>89kZu2UtTZE)grNi`@=%Tqe@S*LJ8ne3F2pHyoY)k10V zlQ5e$U(GIxWl~K9J;VVGWnaZRIz_gnqcAZdKfHgtdvJKT`)>d2#q+8&WQNF_Aa~wXT_RJ5J^Q@E!+iiQr#gNo+?lPK4ggF=Wg{gA2#k;k>eGLJi=y9a@HIfUZ2>b_pM5i4hsEz#%}wzg$k z+e$)L*_Kaw%UKyI6ie-ygVO>hy<5`h{XK0GOpBvJIx#w(c&cXEPHeCc0YK551nWGhaY2}dH>d; zv65B0wODX!{JGWHZBi8I49%2QAHmA^#`fW_t@TIwLYaR@K(tgbDbl-J{r)eltujZz zXIrOx-pdz{Rp2?u8a*=cY7Nhln6FD`uq^xp`aQ`7K1$0sE*8kA0dK5msY^4%VnkRy zf*X|TZ=@=OnFqO}S+V;H#xr&fPz{Y)mOnpABk?7LA0cGFrtGt`RfC~V@l@tIwusM% z3JJ}BB@Wq;H4W;?m!c**Gy(EC5aFQRy5qDP(6$eL? zEeUvwn%oN8+M*Q0un;R)sXKIYn67H{Uvi^K2Gbw|;RK?y+vUho+9(M4p~H6ltOd%| zwoZjd_F8Px?X3}Xy0K6o)9F=#(S(=CZce8ZO1iE%2PktPnguD4Wg%C^bXkau%>ovR zvK+;7m>ej##;g3Mz#U|QP~R@grYXqizTu3`oNKgPlF%!_ss3)Vx@*@l$W0!ZvJP0@ z)G{MnElODtwYi;xZ9@Hq>}n zO{Tgn?z;Hl^lMbii=D0>vmaX!$=|;1xV^B2&Rsf*I$9nasb(GWNW5K3WO`rSp?=X?DZtOC_?_6@^{ZY~&MrtExY~!>UL9*l}w5z$x(RfQlQv{*wW2z z`8l8`9vXLdp2WM)?t44ZM>@_;ZpCrOa3)3p9aWJ(4TrvjLb_##I=$=kh1>VrJ}Uv;YpJ$+VQ3qIichQGDt7Af^4vv+9>5D znn{hvlhFzSH(IDI&-y~8w(=KtxLG5Uw8u&*Bp~-*~pmJ{s zpqh<-+JC}Dqd3H(_lr)=t;gP|gn62udWE6_oqAUSgayCj{fKk<5k6XTrQ$#EhQt(m zCpa(sk<(eV6C8xTl^OaDGefnr5w|641lLSTm9QfUv!cQ+#*V%WsorrR*FoX=jVS`( z{nvO3?6`+d{HiG+_U)HWQTqDX*H2NR=-~n=cfyV;k_QDs#`?{bgtr5+i=0F^E%t&!T{b+6&1cz&Y7C*7&}%IxK8ZqFoSBRcih zvUI2NI;I2OK&|UJ7ue>$u=+93Jsz*-q2kdz6!}&Fx9Ey1BLAr98DBZeU*htctSqW_ znB_x8FzrYdQnnjmZI);YZ{_CI{CS}gH_3!s_oa?2;BrMWVUrp_ivTBHcSvxelqbTa zKd9>f=3jNe&DC%gVUlrGu>Oj;lzO$G%RXEZ+em+{#zP#b1{+i+I^DZL5)xVGOkH!1 z2A|FoF>%OsJ280N?K;q&ymolgfBUxGmD4Nj`lw0wtf*s4O9aRGrM+zGQK7b^-jsqZFpb0SjaB$GpKP>9AacrBy{7OI zr2oWjkh)8!B(Wx+A~#`;wB;;%Nu!+JcG0+T95Z5xv{myu1LkqY-g2*HjS~pK=p=bb{J~s(3%Hb8M3-q^sqOZTj->2>(V$U$At7`J$nbc9*v>l zYlptd(I|Vto6wGF{p_0+kowOmZtmYU9*5D?fY`E(RxuJVQrVcLtvU1z!?`Av0kE<_ zGA*%wTa9eS7R$9pJZ-wX04!9WH&m3?!~4LOWuQR}lCQt+_W8dk=A)}wzP#34RfIqr z|H3vmxJUWY#a;SkKfXTQKZ!bi>oQOC`i^pI-9Fe~YRr$NbFa*7M2su#fs9t3G4EaF zSQB4yto2{K*s2R=od@zDE0MG<;p{A?!C^+nq=ywW3y(#J|8}?#MT1kErzc^6$Q*|g zMdAJb@0uuoo|1ni(H#_pG0kfp6_h{2+I#o}MGO4Fme%^|HW>v#!R@5HoWnVC1`pvI z5?V`{+szl!Z93J+T-xc2iZ;1`BR5w{ldbJ7Z3CknqfwQI!=natg7N{{Ib(WqvItRb zG5T^W+gg`6W&Q0v{xei?dnsm{N;AO-pdXwX?920(GnBG~2kInv8doT7bi4zE=bY%Z zQ2HwEo=5jLI8V@k?C3PosV;X)9$A|n3ErN!DXIu`D?#0mTDqNKT*nyCIvk}@_zXY@ z?rX^rCzYZju!Pok`5F_lqns5L@?gpTvy;5oNU$f8(N9!Os;6`{o7oi#gjp0A=_ezf zy@)B1?qq$1mN!(a!-~!Uw}}CrhgDk+R$(lP#A%sXf47w_53APp*hegP2BOvDEo?m6 zwl+KfGOMCV`({_sWTS+WSY&BYX##De3d7POv5=P(Wm5zm6t2dmmZAYwh{$CTuU1rP zAwB&jshxKOLhl9`lqFq>B3&UdLm**JIyxG9;f%FE7?HrKb#Qdq;#1j-$Ub;Y4MVHel)J5#*OBYI zpd6rTQ;n+NnlMtZtd?bSx3(b2aTir>EuE3gGA!Abh(GpU$Yed5$HF9V80wuy&#q$; zwlyMSzdSc*=95myj&qB-AqfF_1QR<3{c^tVRz!#T^4LY0xpH&2)?+)W`52WhgFE=A zcn7VXc$@fCO~Wml-xcnNmO(6+OGzzx$CpMq-MDO`h?Ji0a$*)G)Y(J?jk+5aLK}WU zeTQh%i1?3aSgHlnij%TuxOh_mV4 z!d<==tzie^HSp7QNA6+8wl?alfh{N}cg%phmX&0o%RSv1CLb{T8X6Kig(8Y^Qi`K? zd|6dZ&mD0@M;?gaDZ5b{`#+;2XG8q_qOPW^rl59)?3zrr{rDOLa;Q-(mv|t|nRX=K z4qpt@F5(v~VVcU^JCuM77#ECcfer{mB?*p8V960#oohHd!x21o*>JJSIuQq5Dfo;y zC2J`8Dn;UIIGNB{r9IGrmei9TRv1nwPfn~>eWnVYYod=8x+aY(rRn())c{alcFePe z8C|iB6x|U+H5bs8QQfOq>JeyN3Ahs{Xq3BH0|Q(bNpu*Qg&)V#ERd$xRGw6HzgXYR zFm9_gyz`!K-iubNwjh0w#xuQMSJBMCT8PxKWVGLB&q+f&{ORcAG_p4ZR}i-5i*2G$ zYmR#0{=UdDPjF%fIjTU&{n9(grAzN$9uo-f@dtb7x!rYa!=RpeWPh>ay6dQZ2a_&t ze&OfudpJ{M(0|YwP_Vveo&$Zqr9e~z6LbO%$2sZ%oCni#RyJIi5A}RL765q)$lhF8 zE~Bp?9d2)^xIRx9vkO~S2OE7XlVB7s3V^%Sg49^}nXhJ?oxE2Mm-S*=HobHRDE+d3 z_WY$s4pgqJ9?56>c=`>Jj~L#AzDG7T1Ah^t7%x%9i0|Q_zixh|4S2w3l=TtF`2n2| zlqtOeVNToq>+r1X2K85Y4?Hsm!aLjxJY#Lt-KF0xC>`iXH7rFa&O_)P(fw?4Do82p za7{*MAsL-T4E1!U$c}|Y6q9LjBT8v2wh_E29hH;OMNI$n?K-NaJMdU*zO@HuOi!XH z)jqcQx|H$X^&^pn*C3mI7iN8RNmE4S{x3uxJ&_TMwT?twhG}AkMB&fNc4JRfFjILw z*P4h24aCrC0sI@99|NJ(x8H5CjUz}XLS3}giUlocK}glnFDD&ufUF(3dcxh22`Tu= z>%7Tb!%}<22}jvS_8&m%dV7V`cC>_L=Qxc;B48vHC3vuED^|x@aaAsk6;Y;Udk9#) zMrjCHz4Iit`@eDzX|`PNp=q#(rm5c*i1+3@JfgXLMTY;5JG+$ zIXGhLH`5$L4FuU#Lg>9<$eZD|i^{r#B1GZo@HKRj_2T?Gu$neZqcD_CoeV3wwu~6%C06s3u;^caO`?CBVU_;;-q@u-8q2QZ zNah%X21HKFFReKSMGUWQ7E70m@JLR#({jRjgEvghz~tP!tX6)B)pBQ=&o0J!k^^B^ zIig$CuY~!y+g> zxiTHElc8rPF0h;OwXjKz<{fteqm#{J1HaQM`Aak~2hwMO)TrU*>r^$-ASj}O#xY~T zZC>Q#$8;FodX`O%kbA7tdY*1-zXq)E7FXFy7RzE%enNtzRPG$E#*1-5KD^c9`4m&y znUQ$?6;qk|S#a8no~~?opX9Q8{yy5e5C z#My{7c$E7AG;)|B;5?y|eAnW|UU4qm4b2Hp+I1eRL_U27Ycgk;7q>=>xTJ)Fh1|Vs zQb&Y0b@~8Az#C1CaG@}=gKD>H;a7=1N1iLivN#W^6l-GAC5VR_~m{yeUgvUu`OFfF7<0XfPs&55DK`pD{c*)S(%G~p4 z=&{lT1~aDk8oE@^d>aHV^kms*q)A(ObKHvFX3}~h)8Spg@f@pRNVtpux<)^Wkuk^a zEj(;+9dWl35Z1J&_f8l7P(P$PP{=bwLW+_C;ISR`7`;O;^12*Rn5BGvmmHnI)ZFId z{H~^a8#O{};3ayGDp%DRo&Ve4U$rv$-_x30^PM& z-MZ`VDpBaPm}h{ihFh}7R@wg7tFnCb`pIC#bv;{W7X_&7-<8mV>9hpcL<=QN8@5Lv za^$I*EfiH=X<9u}v&40&k-^!l&<`&}Jk8>#|CK?A z>*)3P+3O$P{B4UzyQ95i)Na7w!ER;hE`cq;s(KSWKdoUetj`c0Z^5}fwjku>YCZJE zKprmp7R~SXN53W~FXNc(1igm-?=;9m8YJ`!dfpe~-*?78IC^m4!bmblu4lH7rD*a; z^@ZYq`R~(Jy;-)|xV1`Ny}ZSwsd(k$PHdV~QA`hN6-e!A*8J>fE^{nc=u|E<*7>%? z)edAeegl?&|MhMr)-Mt!*{CU1n?JT&WPR7Ku%a*H6-9ls66$1FwmV9MdK)*ZHp4Aw zZUYA^_))NIyV*Ch&#bc(Z_Z;W7xO)+-{GBwK+JH1NrFeu0^SiqUFp#v`{f~i)$(*_ zolSJQ@*Y*Iy_~4ho@IhzjvB)X8`Lt@gHuI8Ro6@f>Z(CTK7~&<%U{t<0E2iXMszKZ z&lWb3rFl({^xBP*6$o=d5}k42an_pq#W*}sG}{DteT_eW5e3e2I1RrXzj*X#)(_Lx z?7&mPDRDU{&$%bR;#aP7^Gcw5j0Hw?&N1d3Si)VUWDj<5>hEe=lC|2lLEgae>|$GN z9cI&!vl_&Vt?G9rC4sm=jz*{JAq?S6hk@X};iRXEpuom75h5f)Z;LZCpYWdT>VCG{ zY-Ubf%sm8?{BdU!HBi%OIF&rTdbV-2yG>wl4)4;}MP{OvQ%j+5#uGwj@bUcDh_ocE zXB)2Lpq z2|C=xI+Oc1yhvCxEZ#EB?c|ILYB6VW0(*<=RQq%Er}e_)aH|{y>k&rxX|LEJJHWB$ zZQP<{ePGbKk0labx4z45>ZLJ}A^T<EMSh8xtha+}jTA)HDP4|@Ds-3BH;CgsWiRnFYBF_lY{$i}Hgk8>Jff?s8k|dP zWx(lBaHpsjU^9xR-Qq00C>xX&$j~K8$?gf0M3YZg?^#Z7)^Z}%Mn_IpeR9xd*QipfL?l8ZG%_p2>(dU{EbRK6nFT11DQq;{8bhOJcaYg<(al5?|HdxUTE)HjkR z)PHM^(Cv!hKkV@dJ=vmQ<=R)*`LPSwv>8ZL#}PV{^LdOnT)MP#4^K27@@ejW|%<@ql zC=<7uK$%v;4d@X|S-ns0aL_6H@gua(ald+3vL5h?T7a7r4px1mepz`WTd&G85@4yF zMVyKII9-bSmY6*eGh|1mH9CpR=z2+fQyDbt#jHA26n;#3^~@8C(itL*f4+N!*YFk` z%T0hlAEc^q=GuUbyQH9>v;!rD{!?X15XS&)y94}9clHxY9ALZ zD_-l{M+Y*#wsKVfv4|iS1LN~6YXeC(+Tl*!S|+3#xpu#CIPZiUu$a^UkPzhP+^lE& zzs>#X5-CdS)p@ZFs=!steS2HGEKn`Ripd~ zTO@IBfT=uAY`96VU-|o(Qi&g4<$s60bf8uH*E?@UXo4zH|1OH=JhcmR#k{O)9i9|K zrs+PT^3-O-{UW(UPQH&jPN?hLT^*%Uia6k0^c^KslTlYUhzF!mw;7f}g2_Z?^L)6a!G7banilLb*`;Z8LhvvV2L$Tyj#CC&{eUUY9h`#z`J5^tIWTr&GGhhZ`Vff(Vf zvaV9`adFaw2A&zYBf<-9DK}>AP^)e$##A-Z>S45-wJH_6>&DC{sE#h*(AUGf44{7e zQaPh*bk-@qN6SHfMr$MUqw>mR+A>IyF^$}_VyS7%csncDQa=8Zo@=+W#6ZbtCUBS0 zj=XBGC^>Ad8g0gC=d{%j*W=Cw=Npr?nFPw3SL+|qTgO9gJ5~iKm#Hp^r_4j)a}cqa znj_QJJilezhbCgvEIBjt+)?-VDZ&HbEjQDeqpgr%hCEua+%@!Zyh2Zwn5I3Z+OXA# z*^(7$e|?vb>NIhlJ1B8&nee(9m1PK4AkxJG3c)B$Z1T({Ic!pcRSmz@K}l9UuYm5`O9PS zB6;6O)M7ga^R`~)v%Kew9Gw+v>lr`Vp6X=wPw1uk zFLbjBG*dE{E2K57wmLH=##oYGO8g(8-P%W#1nZw8iKFr zIOlAL_^hqpg9oFf#x}2lPTkbsy71gvQ457)>k8KI+1J1iB9TP){9-CDSQx# zpN7#R<}D0bf^-S3Z-jUm0<<|_5(y$a%Dzu^ zTs6Q@v^5C$j1k6zA0m_KDwzWPNh_Z3)~h1Hew=US>uNbKwK73m3YupXxm$N*JrG+F zmfZo>Z794OcOGnI=7`TqMQJ45;^boL)rNUT@exf4M%ix`8K82&-}z=is;V+K|uq3EK9k! zyK@>>Mpb%{g?7l+56BAYAVJBk4){S)xSnp%BqRtqu*>z-ZmSgI##)vr1SPZO80Yht459Wlr3w9q!YQ;^l3l*_MCMLPm844UZ#f6OVe zr!yphXd}lQJgUt~l@bb&czJYEE|>A-)6P;Q)X1uoOp)bY>&wdsoQb{1Q3(RhW;@D# zPd^kBxY9hiPs_+c_FG=YM7}hJh*bxACG1B1JSmYm${TjX@1`;HdXNj0z5mjGx}5bgu=pygTdzN$9>>7YuLq7ubFel@xeoPrMJQJjFi zu&Rzxu=t*0K#(ejZmIa%U9AHy}>=Is{H;TW~u9UemliU&iSfE0x)e$jr6rxj;yICmou`rBdc5zlN zOcshBr5W~DL~Sf}kNLV1&m*}-QMikvbF6CD*;&3h@r@%Ew+;?7v7`Nj8rU0;brX&p z0(2e@35SSDVsoDe{W_YkQKq8UQacN`N|O-GmhgENe-ASg)pcmkST61k4am_tcaBxW zG3kI$PWfTIQuKFREheblCVw0?5ZGW405AM+81a)Fw%9=(-N2uyl5Me|m69NU2d!6# zs@Ax1naN_5^^_V9+dB}`&?u?!ApL4+FUC948D-1vUq*%-h3ZWl3qjW(P&9pY ztH2Ud6WjXbDy&Y{=bMmTd8%9h07|f3!Rf zX#f0%3t!q3we-aUF`ed6SIbm$( z+#mQ8oLmkai(_vhGc7W>KUS1R&%d!7hLgV(@c^kZmzHkpbZ9Fx6|teLo7dAbUCJ;d z>~t$#@S4>GqerwxqU{WrLNp(3#f-65lu{%M(+8?11N9R3!aZd{Nz(j!Wo>7?(#~a4 zkn@qVTK;u{0i_sjL?NXwfl?Ie=;I%{k2?g@tkunZu_~6MX$e7f{XEa(hA4yCh~{TM zWf(jth_aK4T?X4k>-+C|V}R!f8x8fGfFlR0P=^*bnivnE^|;I{(B-fqu;8c@zXn&s zOOS&2dKmp8KD-*B_%bFUc1#Nq&S*QuYJMi{IPPg3ub37T5gbDLG$lBLLA>b0*&0#u z0Ai48LBu!?A0k&$*$r{ODjD#iRPT~37_EI$t5VIfUQn({ZLt^ub|6?w*B}sR*| z)Kt*E<=K$wf8>V%>aeTp;vJY?SA_cmKcI1>0RzZ8m_=}h;3bBi5{vVM0VAMW<$LN@ zKFX;AsK{Ll>K};1_2%>pZ925mqLr`8^AeNiSUtNtpC%H{*<>)NVt!FvHt`#Mv$obI z+EcRh$)&!`KnjN*qPv_<#x$){dnwpL;H?al0qTGt#D&TJ4QBJ`w^8k*vi)<#_&H3EAPXeGw$Mr$%j?xkW%m4uzhqDx5#N2=#|dTA}< zhD}9dvVF2;04Y&}sL#)#TAoYnZk!4!ayRJ0VMmim!N>K2qaI5PEjKzmWXd?nR7{YM zH|SbVBRe8%)P_B&KVJ!L31_6)+ z@SPUbO^F85QyVu7L1f{Kuk1~uDAp)@T?67hbPSMd57Hw6U8?9_dZ(xgLHmuyyvbY< zZL15v>_<+|L7d3)kNF^RRY&@i!NJJ_`=R_CsaM}u(=s&8$o^v_5e;KY<1Uz8w@ERR zk;Bg|;YXzjeC>}$qoG5(53(a&>2b>pF}57iE}af=0weTO%oa+d0~G`F66*Apin&f7 z(2$HP?iBgxkdYTWen64ARFsK#IQ*bw6z9WsVqoG!qHkcGyF|c0vpj_&q}LW>7(GWF zM127RVI(>CT5@F$AruZ>*IE)|3_4_pPLR+KfQ1N>s6$}lX4wU1$uD7^IA}oXzFmPf zW3?5%*UxRgGk)*s^kf%Arge2*DpEXVH2#Z?Qz+^b8C6}-4Cf;aPHdOMMjW^)9D*k;cMIr5$FnRNZScmUm{f%t8z5k%;)Fe z3DVE|N8@7ur-%Fh=lE!RfB$#<_pc5H{}q3L8b;j!CX)y~k=hKC*lhyJguF$_=W7wJ zm@n0Ju1@u;n!wEtI&?r0a5G}`4thG=)Aq#omKRen%ddP_KA8wa|3rR0)_S26mJp0C z1TW}1uSMo=aqJvjQm7nvQd!8;8k&ElA}N)iNYr33QaUQfw0pc^z_@ZLTbZruN|a~$|e&*zO>NiI${%vMocl)+WpfB8Ar$1J%^VA20|JBnTc1X;7Li8AkLa|loM7?gX zQa3s+$-+CY+0W~|+-l+4&d@DZ52h{NH1tZCHPK6fhiV6>*j5cWqs@joyS%}n8J*RQ z)vW~PTRp<&NK|%wWrl@w_QDo5&tq_Uo+TGS z@$3^akC0m@Ldy5@qgLnndAFN-)0L0GDJS|QOEfIEd_BBR2{N_vnQC>QwywOa61tePjLd`7rIwg3woJ|qb7jnrjAgry=?!l zie49sbu~edy>eciVE*;_CDR{V;wdZe$cnHmYY2GL(T$u45d1AP!r%U1(T!8I)0Aaa zj!sA7r?d5X*&N)vcUrB_HYac{?pcTm`tu$o7Os?pN9VV39EcT&Cx6OUNl}7S8^t&#q@6pex|#h2shCd-WkMsLFS}YMzQlc zymY(wV;1RTKW?fO0~Pd2BYtm}K(lX{VZ=4?Fy4<>|%0)ub%c7N6s zfPL{(xesm}@5|NO3O`E+~0 z$>P1=tfy)~kr~MEsYeH~(EOVX)v%eMx}SmpmvI0-&dBW*dJEkhgdoHH`b7PE^qdDU z%yx194X)@Liy@uT?wvQMXw%VS$$reQ5abADEsTi4K;i2jb3fmDwQzFC>oD^?iZzn2 zF%}(l@akf@H12umvawQo4NVrpXddwwZ=C5Acp>)cl(0%1B2D38iMrG* zmjw{A0>CszuDlY8c4|EZv#_dvDi>PXki7Dk9|1{$XF~JKk0L4=pX=2-e#i~cFEOt4 zHmbopI8%3csJhHXYPQ?w=Tpv$=+pt*=I@m5$oiw?#xZ)fkemXYlneHdr89upR!_=u zp`EU#ifklKkM`dKx&CVdMf2M+}Vij9SC&Ts_b~JQ!$tg$v+Nbu-BNX}js)E1~$>oiPW7#Q8L#3RhLz4dJ`=U7^1 zWd62NvNLKr$1K}7{g~dIUTik!=Zb#z1=ky`A+aWV*_#(nUSyM1-88JRFC0i(#1R&z zG2gI)c2#pn1ZvX>884=68=3$s6;XOo_WR*sxb<0Zo@nSfsIOhP{=Zqm1-qE!Z z+iPzj3DDLzu63!%<+2-fqqtbTP>LA|2CY87Twc0TfurW?9^3TB1TujvbVZ_%RrF=` zB72PT0pdLADU3%ePzVHsK0vsVBH?7Qq2R|vXCQK&Vj$C?D1njz@oek>b|_E{&%k4FEO;@9sx-64$%d? zSVi?e9i%rU%@yL^z&K2V&YngZRWF~G3pA)rM;O({VU+CZX(D31P_;c7{iE7mWF#z* z?h1?aryGnK1EU&;WbQ>GlLjM0lD5Pa4l^gmBjVYlQaqaEwk@eA%*zJ;G4i^xLMJ$& znZ)=>vOiwBbU9Xz0fm=2F$JcY$0&Dl;;HL-Nc=@H+$_LkzAo2CFAY%qI&udXhX@bJ zCjoT_3ORB>NG7;X^#`!hC}h9!lY!vU49%<$BKRGmHbi(i%z$a`zm(-0lMjVaKx}`- z=w!ap(0f6x}B7qijxz#v2m#ZE(`2wJ}?H{4Rz$&-!m5`9G>@zdcqlAZ)P)bGdcw#Roqid z)jcZPOyAwp7bU45=GCNJH04;`Yl;?AhO5zJ>z09!)|xZ4ijR>pH7U{Ig|QEllAcpp zZ*pcm`P9Il(9T5PkkNH`se^T~mIo)?sPJf4OL;@!@B^4mBz1OAS3 z-v0FCm)h=_5h zyB;L&)I-?EKZ@n?-40HGFH&b-(`#}ZvB!5f&nB^|vzmaSD>t)z%ALqR?Ud<$ij{jo zDeR7i9s1xCW&acI8=7}&1NVqtsc*DUDtFGO*%ztpYksfSXV0sXRRM4r`p2gP3R+I} ze0|gj`xyQG7tFAtGn)zMgs0|6Zg)W5X-*<7(1Fq#p|5}Y;KAJtnj83m>_@Ny@@&&n zN=Db*=|#m=6FJGZwVB?O33i)Fg1*J^7C_!uUmn>%{Qg=GKCZeJV{ zj0Jaa^hdgw@2Iul*4&|9S&w^}-9;tl*YH&BP<_i?_>pGNK9H2q{OXfJ^T4}yz0VNYH^ADGIy!9LseherMC**Ccjt2Q`5{YewYW<(!K z!1$ZljB*4D3P`7*?M8)z>l1W98W8Aha_Dl#%-?2nl7Sj@sEhULa!heieVzsB#sc;# z%?-639eqx)pt zWT?fz1BN!vMas4!w(_gZynI#8iw~$296fyg{Ka3#kG_BQ{K@#qvsYtCf;N8j^y{au zo__cE>1)UNU}cK6*uM7C9f6NyiU*Sq$+tBT!cXgj!!_YZ}llc^r zcWNW25fJw>$YB5~Q!L6!1k`BM!9GKKRCcBw%njPI+OG8`jS)$Hkkb`mrmoiMmQ_=c z>;;_@W$%(ijy~zy(%_X$5H)aEGS!J*hQ9hEkzFfpsoJ%nFsDi^+ikk7?d`CTY8#3h z{2fI4uou2Fp`z9OsAAJFAEyxrj(*X>p2{a z%K=N7w~;K=6EH5LEOncD+`u}X<0xDsO$I#hCx{M1LceoF4~_@ma%?8aumFnZIjN@8 za>03(jUTl3$pMdUyr?LoC+<;*;WaX96Q^WfBQuZ3J{Od5;_vZvVG`C1E3CAST39Et zuAB^R+LHEKe0sp+At#bI1~{5154_ zjDhj0-Nx%o(ai@>SqRzn@#`0_9zA>VG4qraKl zM>j{VOh*Q5zeXLXx7wgAtYjPx)PB3a&qWRuYyS7*t%pTxI<-TG!xZztQ^=WZ!rYY;uf$L%5WF3MI2}4bs{>jEE>ZpUuDF3grog|fIfIDbY>wK|OOiY(QchHX z-s9TyYH|h&55-T^>PVD5tf!~S!w*GICZ=WIBr`II2QiP~A&tCme7fAOW71m3VabX6 zY2h{)mCZy&Q>I|OkM;TCyGZ|x%E5vuk_H~8LqE#BZdnlv24_vudU82~01q+NePn;S z1f+L}pxLbYP&S#22(bR0T`pdA>3(s;JGXt}gh1)h z%{>P(uTd0ctyYrMEN{U$^fp7?GF&boZP3i z?`)=~SEjl4YFk`VP8bBNXD@8qfV!RP>@ki;B%T65f>edPU6H>r3=+x*pmtTedqQ9& zs**){p?ZghD1NEAJCLR=suH2|n@*kt9|)Sp93evK4Re;08-EQwrzUVkC<|}X3I&*m zp#DZrANd#qs68$Y&m*t!&Z$pt$U~>96VZ9_2?_QQQS)QI62$%TLZ(v#d>2XS)-~(R z%k^$FI1*(UP~g#K33qD$h-vllAaeagWBI6(@#&_THj$R1?}-@zE&4|NHiG|rsdk0x z3C77rux255;CQa>mlQ4k<*yCGtj#FuzPM{Pk6{+Gy9WpjWJtY~@VwHzDk7MWwSfLO z@Vr{?vM?{Y4WsA7g51tf)C1qgARB0}Z_|{z6{^Win!m!e6VmQ-tXxCaLEe6#t%HS`k3=2E z2AtC-%l-Ke-$g7tetQV|T-ZSQb?esA$Pv$#G`lM1M?ET~c(fkX)H}|HVM&*FimMww zK-#?AXPW*7zfz*T6Ugoc09(b}2w5PjO$rcv`sy2~h1Phppk!7f?uu6-<&iaF0?Oxj zxpNdI%W!bRQ4nTRode5yf|)hdHC;_AKS`jHE5HJW5Oj^qgUJ_~$#*vrfw*(fx|7-O zoN`clb&}O^eNjo<@kTLOtMz69QUKiS&Ah%~_Fs>`qPSi!vEZJ%lgqgpYKg{1wTnTJ zT7ruqT}>nyL-dbguBUiutVz1|>Ot#2&a#?)w~hQC_ef#tvgNGBHv)#nVVqh|)qBR7GVq=Nj07nSE`G>)@u9^9`uvrvwl5aE= zV4mJt#;MQe2mwy;SkX`kP=vU#_t^fsq58u>JB`396Y^Ah*;9mn!J8Wm?Xa!hm5|AH zDxj%Qx}$xd!>B2;4_GWxcmx{nA_OoAz~d1sSyRLX}MHe!Cf&i&pbieFc>#A_U##y zjE9aMc78K57J`HgQtj%|Iw`ljcT82`TYK4KEl$!xFKRCvw4Khy4Cq4I2VpP!OIa>; zRx@2IM#v*becw*WqvZUxI69nD{VY|7O+N#G0NrE?F z324}~E$a-g6I5{)&6pN}9WUTJwynMs=#rq}atQ&nonEwkjj@pQCN*Kpi_4g;j}hkA z-9Dzq^xbZGcmDm8G3^eMy~R_~;gT&i^0>ZF$2W#NqgPQoYaI03@_#<51=_P92sm|S zma5^jI0EV?(qW5a3|@Vpyx1(^D7-5Fu~Ga@1IRz`)zKei*|$LL?8VGbt`wjS2SGqM zP;E}W57v}atL#h>W;mwhhb4hqfb?AJE3QVQ#$*o*%2H8kE-NF&1$sIx-<<-HsZO)~ z{mE=zsLR>EAiV@DF$qj_!+LKzJ25wSo;Q~Uk;8~CGBN8>|AHO+EKfwF&vCIb*|CVE z5_Zdz?n9xr6|}3(#pV0Oy9H*a_PT~=_8`0Re!n*Qa70_xaCykA$n*%S;=(|nMgugD z*(2YMWzGWSe*Dz6V z6j9!iru0&?sWkK|XlSm!P2c~8TNM`)9VJexj)04#>|>)W~}-qC$RB%%#HIlkTEXButt7PB732ecg&a5hg6p~=FC zI;UFaA%F7vxgrbi;Aj}Cz{UjBX1x79RR0IU3_rjo-VRw|;n+XPNReb$+Yw%IpdN0BK}wZ9clxl6SLa zDPwmG!>Z2mtNAr43XTVXtK^(CZu=|Zwx+IZrH6Yo*Wk}VKXgVW_&adW)a8fxDr@eE z$skF@9!B!#MwQLDIH~&T|1inK3m5Yvts2ZHp#Ff@+LP-|KwKQtSVc9&e!Nn}AHxgV zLg)0wj@_clA~kfVVA4_=SH${Pr7I)sO0?i(b&WGLLI;Zt5Ec)isnb;p!N)1<5T8Gh z&pm|*!E(^X&Q8kpMackkdI9Oag)ep`LJdJtb>KqsymgcOfUF)YfaH1*)0KG zL2Wh%XHjvO9X*STt>jk`*)59i(sZei;DoX4@yQ(Tn;+_LMt8Kee7H`jE zEpI&pFpBfddV>-(=9F$AQKQT4i1!b}4XVcgq7jlp@+NTio-0*#8It=NRIaYUh=HiR zbI$8x`+-(0czNh``dW!2{b1)(-jKDQd(rJLTcPEK4|{%ASEOqM+fqhOinF|THM#EP z0@Sx&t;=a&R22zlNy5!Pw#OW5ywYd++Qet6nTO+i#of84Q*v5^@0A@9X1cVC=(E2a zSAnl$`?lbKA*(v@+h^ct!?_jW?5}j)Qa43ZHDSBApyI-^*RIQ8S0Xp<9CVIS)uErL;Jbd&Va<#tp8Kb#w`^Xd@kxPNk`J7kH#7&OcoZ0qX|LNhY zr%%SupFIj&;TAv*%G2K0MMvzar@!*^kneI^*L^mGBiC1U;uX7^_+~ztUcylg_%A9H z(?v*4&KRKA+SlI+X+lza5c;P5g|2sV79wupQiLJNoGCdF~@joyBH4^oc7P5;KW7Eo<(M}# z@Zj{@3UnCuD>0&c(F3(Tu`S^X+R40`Za)8$43@)0gM+3>8gU5h=nY>p=Yd|F? zf2#RX7OVLs{}iv&n0_!Pk1hxM#C|!e=lBQ+&KRsG)rVAd_zJnVQVC294N_ETT;sA_ z?XO}?n(bbgX&kDu$==+4u=`pZaM|OkqIvwsFS{jcQSKG2XI0mO2^A3(cimLwO>! z9xxrzLX}8KK+j>+7T~k>C}z|Un+0O2q`EjzT&jcY>N+2hMye0HlDfg|3>^I{Z3jHh ztpCoBwMHiuwP{jzIi&`ErJ8oFi&NTSx}5Xi*e^z$x80dW7p7?NKQ^UE_r0xuPEBrT zie>;%U-^v4<`cjGXAgvMEFonz^Thf=PWNrwj}{81X*yQ)TQU0x9=^R&?{#v0kB&`h zW<7HQ<>N3~F{{FhtD}9>mEJY-CG5GWx1HX&3OmvDSLMY@5sB=r094NovZHfFEg+4O zxgPin5P$wt-3iBUMODvWWl2RHjvq_BD=#TOwQkJ0elquGea$#O!ad|1WypTx?{eTp zjtAKvvIk-2YVfe4*WVpWLPlO2EJ{*O2M0SUPTQ!+%K}M3=8Wc7$gQ4}=Db)SYiCp` zc*)o`Nh%QBAqrT0d*8V;!*W-uoGK=387=E&|Gw7@PF8v8OjpEAL(2SUeH0NaI*Ymt z*K16yn>0*etwRbwPb4_NBcM8k3T*1d?n{|Qu5juHYH)+vZ9TL6N>z1Di*wTsdpCft z>qz`??5-h0v_rs%;w0Z!^`?P%5eh;kISTl1_}gWD@D)xr`wl#(z(W*nV*jvlpUlBM z)=&mmxz}U+RwSG!^=b!4!yUJ(CJwN+yDutGn2Gk?jq^uNYyY#~?|Q#SDc;fjV|2EQ zJtM*cgF23BOl-`Jt13ykqHR&3`QbRe@+7J=c`CwFU0bMDKsb<3ais~XCaB-Q(?WmA zS3E1hO#>V)+q56hR$$86LA8Insm6*|XA}#B8EY;Zz>f(v{4I5%=zW6L4|Fes>dFtq zpIs`EvYMdZyiBCB)so>W>&c3Nw9W+X5z^U7wGh<`LqzCCO*v5ND(@!Fr%oZu65>QPn+tee@h~8P1P5 z%X)5Cd-{SxI0f}CzBuZBmbYxR@XWQ$ugk(3jZ*Mls)`g6fL6e(kVDlo1f-10pPoYY z2)-L-tE<8lrP_3}9S$aq(D$8;5^U>QFievru)zWCGlARq#i=i{w?^8-C3L_}(+D?A zNGS?Pf_wDx3wWp!_X)8@G1wh+x~emS=ISD~?gqo@$=o5FN#O}NVTiWzE~67~4lFN`}B!o5i}~jEYdv3W6DsjYWi0hv!&2 zd2vqX^+_>jcnmbyM|cp$x}1&$e7oQfneVn-96_`%m-9>gHiX~+bLQwjoN&&zdgV$5 z5g}EhIs#_Bb#iyPa{6%1{bMo0jD8L31 zE5S@7hLKlWEW}&!;Zx&=ff$V!g$I@CPRY0Q*JF$a9%16jU!?Z0s@&hY!JGuHSu`oI zW)--+`r~Xbk^16Km-k}5Vsct(cjCyy##?4;dE!!>1M_71vMk4HUMp@ zcC!)zg2Kr-VmtOcME5cWrwei!vD&dn=r)`+=eRxMeRFYEXXVWv+wJ zvWM`q>KsiQ)Yq%COd+$dcNV6tZ0(gM z6ufATUZhz~EaH}%XWb29Z7`>41j8vvb_xX(kNPgM#15&v6BS18IqE27u7E#2%UrOm zcuWP%gGj|H&oTN=(&*jg)eUWNaNEm%;EY&WTbNy-l94f133uS4W&qPz$|N5hxf%gr z3u!80d;1L@HD;nrUY&ea*Y{3})o9h^gJBkXLp?Qbc$OlyT8y3y#WYs*h`=Oc>;?}8 zHVnXFaZEIV#{};}?K2DqiU+&YPH1)X6?enTQn}wvjj*|Od@@)JF+uih5U<=%S3KJe z#OO3M8)5G5ROM}%pP~HB&C3&g7vOFcONnL-eshFs?S@eA`gJndxhdL`fBHtgwE8mx zD-$*$QNYb+f<0)cKz3zgwHf`#^lvb@-ja7S;UsS+lP89A>C}TZ-hQEzf}+O5J^6?p z4e$Ucse!3ZpCN<4F2zXO4E-8`Y}rA`h=SmRHsdf%Dwq>uzq_#XUJ@~qnh%4n=?r;J z_DWBn9U`sO1ZapiSXX3KUoskMAa6FcI|7n^$Vs6{@L<*l(xojzFyhqYCve4E=RfXz z+i?vNu+6Q@;FS5;EAg*fj5yX+`(i}B9J?+?9QV6@F=Uy4`^B&W97S}EFx}y=omY|# z2RkjK>!!8SI#P2xEh82A_^bG{UPPZuNiLD_TVoaK9_r1HwhSL<_T|Nn!Wu^L z(=sHJBPH@H>eM-ql>w6lk(Px-{NKhzd@1Sae4bkOhSNx2ybQiv7){n=#pL}|E__P@ ztMsV2o332u3ngn%lIw{8(-{P2o+Il<2QUHtL`}=3lSpqwjWk>JSL80Hf65}%dd)GakiJm-r zpfT?Lfhy8KHu3KpC~c^SEX%s9kq7r&lR)92;;a{i}!J}p+uz-+{pP=4K851hKV>o#>aWOql;eB! zEsvZ=RRa-f)U^0!2r&C_XP!NI`rVuHtEZ#cWu9Fhn3$f zs7GVki|%5d-gL1w`F8q^6y5^Dw{3-1LZ03wZFNR=&#aiwLDe1EK!V1Y*Y!9&dclX7 zUlUplUk;O}UC(NB#N|ZzKOuPhrMyIIdT`U2UbiDSh{ncd)Ae;>yJa_!U*|BJ^y~X3O?5Er;QehQcMII<;G?$#pK3M!^pONe z?pvM5@bus?yPluj&(PhTqwQzu7UyY)GbJ~oIw6d@v7%l0)#vG5=)O$X-?sCo3+%K7 z>+L*B^ax6TG}?{gBKbw^o|bd5Di)`F7Qgy!dKUC+70Bm?8NhGqKgR=i3h?cn29Gs~ zyBjJEeu&@M#y!!_7Ead7@ry@)9=(FD5Wguh&#V4}o?%@t;lC)%dVVH<20?#by(_bq zm+P~7kvAERY6x~j!u{h>s!9e!^TkvQOp~;D2QN^|&W!^<$GJv)a!4Kb9;jc`llyKx zWwIU|>;us;odX-eH^GRFDIwM2a|hn{dJ4_{KD*yJi(pPgw7>|FCZ`{{E!$MN{zUw%lkWXmo?(ag-fBVwk@wv>`GQ>j#DDmBfsnHPrBMY+gD7<%z+ zp5>(%rD;}1Wt^qOntYY%U&U1-ZuP_BVxDJXQRvU(Y<{PHm*Td($)mY`zRE>3iPKA6 z{jOMBn_?&CQF)cb7pez`@Hp}gp;!1zmI(s&c_xZ1h2r)17mGNVGzur#rG_xeCW}NA z>e~l;JQ6Ve^mCLf#7Mq6xcH|Sm+I*lumPX;ZcC9CfEIo`@;-{2+B?-40#gHvm%567bE%n<;Hx<`i?ZswxQbFBr1WHz@8&`bC?wjE6v_ZsEhQ91|8nMpQFr39q zq-CMs>|_%$bTC|is0zndV*CXLu2}D5mI`DlVEK=q!}tH-e~{aM9hKbMLLeFnYOFm-SIri(8O^d0!IpQKJ zyc2jg^-hA5|2o_YcmBS&^M3#1yG)Ffpx)?tFj`2KCHJCgK89 z7VM|vWHBL{nB_%!4IDL%HR0p{Q39CdS|p5QxE-YakMMrv(Lk@GT>et?i5}AGecBvv zof5~33!oL^-zeokhlz=*B$FT%O1NtJ{}e;PD1m<96yJ>jJk=T}QBin%>2;iEDYE{t zm`61G-~VHv%MLf!^t>umflaR{a}!FF8@G!#C?KyEc@bZWP46NDk-ir09UZ|8`EdLW zCd6hXkWWv717fo%IfeGl&b+Paerp5-ARv4}2(Kk^1pW~ArgaJaU%HW5<~5RymZ z5+u;S7ICg5-hj!m>G}L4*yCbHvXtK`O?VAQN&$e1Y~7Wn%sMPNHa%l-b*93B;y60N z!PufjQZf>u!b!dmUFC<~>%T#xpHgTOFnh9?C*oA{#wk`l8xg1x()MhrFx7u`P;aB8 zs9@nR&dzxLudlDu5JBOY&9c<{BJOUoeBwn)^m@}Q_Y|F=8-SG}V8%JQiVN>5N++m^ zAZQT*hhG;8t6oOPl%+S3PiIoZY|5cHnxlDhCCnK$g5HsiK=RBxSKT?+^~@GbMmPaL z)ic0_9>;}Q+U5X_;caicD9TKK_vkwzgMchR{A5$JD>rd@ZNND>K4< zV79MVMpwix%a`6YuA$IjvmJN>wHD@m>BT9u!w9vTsema4+{~l65QTHS-SqaSgmPR& zX;cjJ)K{(gXVh-Yi?9Sp+C$av$SaZOsQx4_aN(MaH0O*oqSI(5&I4>x7bD>3*w^z4 ze^zEPX#P9 zJe9n4S#TW&;J*pJ`&017AFer}1i+$$iuW6(pcUwDfaoCq#W~LqDM7B z2AHNsUU~0@nCp^qlBSZ;aF!Ra%)?Obm`S~P62acmyMf<=#RnBtlIRr+7cmLsbRS0?mq=_!Fh1Q9fZ@HP~qy`kKhqX0> zx-!vFc}8ob`E%gDUc6_B(*0QNswn*3B8T-vcLsk;Q#!yUt{!Fz(}yd-@ZoUfJbF<9i#Q){F`&2WmGDu9$ml| zYf%EnrQScdsh|c7X685o&c*sj>1h-v2oHw=dfsasvnL{6pT>Ds6t98*O85+m4UlsT zy5b%1eIDHv7HC{!<8&eFVrzoqRvl~1VmaCL?rbpineawq_mc-bz_c18;v?{fCg&k{ z3<{=LQKQM(+8Qgi7H{(geqiqAmF{*X`-*%UhM+x^VK^v6G9AeYy;*Hq4V4}k*$^JS zEytxwgBV$qSsferE@XKCQY{!q1#m13$+sH7{f^S_gK-8kEGnY_AcMk@3uJSK5dszl zKEafN2xijIYEUiyv#|!|)Vvf@z(74Iu_S@*I6nX!yEeY!JeYJJ^ z_lDI3wo$;G6W9;1?3Y9)0m5%WS%S&I@I5S$q=LqnDrYn4orLki=mY#QxqF-s=is>z*nLj|RVC6ya`W(CEi zy48EOW>)WW8!95@gTRmp$?B8}(7Wd;moSP4TG3D1l&s;x(2A+SuZ%X8wTf zYvmOL7AQDYBf{orJ zsp>w3DrW7K2x9SmNYS1hAp?#ec0m zj(oYlK3b%Q91SsWJI|HpsJXFjOl@yGn`|m~r{lAwY_~W$V7t6o8DR|P*ykvZaqxws zQjsn4u|UHCImR3%D9WO%K(V;!_(~muopPQPbM`fK4kAuNYLFmG_Ym9J95`cd)Us01 zoXyjQv&mH~ON%TKHaOxkwCYtX*02T`h63ai*&INSziOYk8T`z@?9a@=<7^^E{Guo) z*`ll-Kx9K*F1tuWbVmWl4uE}l?u?KPMh00&6#++*RHHgkMkd-vj8|g=W;WEqzTQ#} z_WCig_e4JS`mnOsgPXl&4DCIdr#%N-x8ShJ(b6W&@(|i2A16CHE2fmx3CUcEyfvGU zeN0hp=>5Ud(w;|6U;M390UU+nHZ~}^j!90y#$_hVz+W%=+m9H4A zt@!b$A9weU5HvGjOS%1zCr1azhkH8+)%X11pIMv^PALX0_}3yU#o(5Wp0{Z9tdZet z*o6CF>Ubs*Y?JN{x=FGkAX6(f+L$!eG`HO-E%nU_SO+k^LfA|Js3gst0mjk|9HoNA zd=kOXk!EfnSP01t{|@x~VWr>VB*wtK5Jbs*QHC6uH<-tBGMVfjjFhkn(U+k38N@<{ ziyp1V;lx=pdS7 z%n9|$21n=`Cs`o|1|32E$LXdS2|-jLOEh|^in}79GIhL2O-6@r2D^KoKYse@ClfL{ zNw&-~hx>44GQ!!r1^*cnoHj@lUG_06-)4(+vWE=f zcImkMr#s|mM1~f(VvJ54*<0`Y--`3V_x`O9K6G=LH7(J9cUE<_;~S4(PIvgEBXWZi zmH^g(O6Y_rx0-^&7OSj(Z{7RIqadf}T1W9s-i9ySZFtY~2E{)!D3pE}d6P)YvUCf@ z#<0d`FVSUUg?g7oTbH49TbC)=1=pj2h3}C)mYNMgdPB|GJr8c8xE!nunARVYdjO3W zzFbV1Cd1_tFp$3n{?GZ(DefPCPK_7}VFDE^0&N&#oD$R&Ft1;~Rx%5I`op$@S?d=C z0;O6f2+_6j*8wW+pP5nHd}=M2wQ9rX$QG0ob7M2${cg*9^BD0Xh0XoLIXS)s`@v;g zns@&Y_yKylqjIoR4vnmym|{upw&&KaB?Gdr=6R~1S(bP8ay2g$tdwqZLDj%&y=Sg8 zdM@YgO1%bc-`#5lzH*OY64*T^%i!1QF#uOK6xZ&~6{^s=9XxA4m(Xs4@kp`l-e&d- zcWA~3T>R4&9Kd}537E@1xDW*&ImT!GulH{rHog0ehrg}Y5vhu)In2I@kWdgGCN$jnU7iwA`wLDY35fWdF5?iXYmAeIaCD8 zjE)LX_@sfE`oKEa0jme9tAqN$hH|WKtOHx7XlcBP3POz>C;p^ezGGYS3Ro&BV~o^f zCH)(KivN0i&V-`WZz36Fx|n0QI!M_?TE>a@Ciwo(!JEL8>kUfizTp3AYxs;@3kTOQ zQDW46VDl!f;|wG>2hC(?L)Dmqmy9a!9%211e~>}8UMg-p7?tf0Fs#Niw#)w+&B7c9 zNi$RjLrSEM7kLitgejdWaa)YdSe-(YzBE%n*GToEhZ!s|>cxi3LFIr_KZ&x`>WtM4 zQ6^d>FWXb7TZZw*^g+2#7x)fdL9CCMG#FfG8Cn{2haL^g3YEW5A5n1o3QgQBQpS$I z5q#I~B6QV2ZhzeJz%?tB((@9eb}S4vYib=m1YP-3`!E~N&S&5^4d~Ga33-ym^h&wEj4EyUA$;qLayHX!T- zP<7n@bkal=<2>eAZs*w~*4=jKEZTw>!`-NiT#6gnQG-U&6;!qsh+jon>r{=$H*bvW z2Gii2V{EKtF}0}KAx)SRr*fr53BarFh7rMJ3W8Rd@5W+C?iwxqE48B!398CZqlBt& z6NFzbrHie;JSPnRn-LD4&s+IF|GX~kXA<#2F;C)h;IEthhdzXx`s?=w@CQ8h^b9oI zc%!G&lV%dte;Nfh-q^(3c+-16ef6pIiv6>3fv4;i7rYo|Q=Y>`=G_R`c)?x}_q_Tv zBvlc&K;Us)CU=1W?H{nkOwf{h2eR*4)RG-|`m{btM|m{U6EPi`iWqQ~8}LBhLRYNa zC(2*ryqKQowKHS*CvB6Qnt`l|ZN%kWX1bVNh&-V4nCJBV+_N{FjX$4Bsm^0%X>x}F za)@SzsSe3@A4>MTB;!7A)tAWHmes5S*pS2b{#v)G>hYs$1}Go+hd^6CP0Qodr;ri> zM2Cmed*jWbJHjwiPg*$A35+He#Uep>!V-kQl1G*(KEGK4Gn9CA0HCI?|6AmlS~#Ix znJ+}}+=%HTWB`g{oZ;%?1y*^#b(!wFHANtmm_R+cLr5%NZ+o3sP_bdLL$6}2Iri=j z1mGz>85sG;KezAU-8Ge5&gLcUp-$_FJbNT2FD>=uvX}azw-9ZZb;rWAVY>y2y`hpD zDIkC)-tdcqAictXi4wH+GNIBDTO}yup}c0`gUJ&cRpkzKcnUkn63_Z^crXd(x;9Ky z_FXn0H#dZflyw`$P~UYJg_eN$8k|)Dw^>ZR{(8Lj%QGG8de6yo46)8~P1oczF`+Ty z`iXP0oTHx+o=_xifl|Bf>o>1A)GOzt!6F-*ugz(rKdhO2;XA@&WuG>%^SgN>%GshodQVOYS=Q;Cq$H$z_qOzIGgZhYirCmBj36#zg?rW?YU|FGiGUP=OoV$PTm`rTJ@`X_JH7O4Rx3=mqYCQQu?1%#(|4U8duJTi& zOGJqRDn9>m$*WhWeD6{T_yLrFR!!8$Y*;l&s{TNO^ftX92!G6Gf)M9nj z{p$ln7q%QkBze_dK?!2tHzM#I2_pk>jfpWy@!dioaVn~oxTi~vgDw3u9DMp?Zy&X? z4psvH@XIBy`=6uSZ?cjrfn!BLmAq2HF!Ed)qNQe&tFz_ERlh5%%(lu62kL$EG-}1< z7$2gt%v8uS_|jJTD?^V>t~1i}6Oj&DOANigc^h_*D}_2hyBeq{7V+QSmedRFPRc38 zxoEZw4AZ=;6|T_5IN55YNL9~eU8qrkVV1BY)})+iURW+GTfwX#EGw8T(z5$_tFwn; zy^EFf?3PBZ0aoJ3`1<6SnWeJFz=z5^OK(o#%@d`M3&69BAMW>S>bO$?J$_z@@)8_b>nY4$(F(Ty<>B?Jiw(1$yf>-AD5|9sjW{n7sd}%|BwOi z8jT{KR?iiq%%SlX3s~6f-{AMZmwaDm0fe($X4NE2NB)fVirVx^8H66AKXq$Olq1>^ zP~$ADWqRKV72o%*p*Js$@*w+QmkzT7P)M@gS--dgkvr?Cy%GG-TG^V0ojrCal0DOr zSR^sFM)Fm+v&gm{c1?+QLI*25RK;qbdAlvSUZ~ahYqCCwnwa}oX+>q$x@Ian<>R7# zjZP}9qONW@RKaGEC+mV88eqMZ^{=D>`unL7fz4Z8ymo*RkH#?WGP#ptNDvcB!|P{+?C4BvGT)Ko&e_n5(tMXf1l=R)dZ`Y8wl$Zn%<3%j+v~q-$&Ww7f2h zB3)BTrsefI)odx7`YPK^p6{(%NRw!YtBjnb->&RUuz*(5&wTwt#uc+kXZ|1q6;ToD zb)|uao;tk=!?l=qL~`qzMliQ}_q6q*jy0E#WFly@ar*<6m{x%Yg=1YXAz*Bo9EC1Z zxfTI}^!MFkbaWx|YvQ>I>lkz*pB{QC{0-^m?>dOFNrOl$`3(cPjITwCiOE%3X_VN_ zV410(O*cW_%kHj^ymf|&zpC!8NnRpn_!AM=}p>9a)%N22)N55)RzKZy2 zhG$?H3F<2|47Xx4488m9GYk#mB^ZVpYGIi0@aW(jtzful!De*e(<`FfBAsl>v|K~w zA(ziC_c>@QkBloXT|i#CzmTbGycWwa(y%Oa4(Q#!dUUL_Q^pk}Lf8OD*B5(sYp| zA=Vr;Y~Ec)LTJe}3zk=3N%aqn{yjDWdt-?YR z{xatirLjai4+sVdHmB<{vMGkBXXR@Gh}VyhSo7qCdbUf?nL0jSq$%5pYIijhpmQ&g zq8upoQ#OtgC27bc^mBuyJHV*R|3)4sUkiT3|DQyd;YSC9(PdNoZXbl{Z7orkNgXrl zUER-#!jy9cg;O-#V1Pk%QMC4@A)a-=+eYxIlb||{;!{bg;&Vu#{qSucg1$sZj~&z7e_r>R#awxh z@}D71hP|x6HH^tj7OBjmrd&H4!C%-^tF5$KPx9M8GZ(8)_h;lrs=Gc>R?P#!s+c}}(Se?uuB zuRXU%KmM=h{kQ3PrwKq}`TLdK7QajkC{LlaSKhWa<4cw4q^DnaY^jM7*w&MrtZrVV zOXED$Bc;fb*D~{z{**LtMX21kyt)~h+6XbJ>zVUn!PNP|zSSqJ}`2s|% z8e4fJwIRGn-&f4qSNBtOIjNqt>)g<)7v4?c$W(6m#S8SF8(OXRUqBHNiFH&9Up%j7 z-sm|uvh*-&OT7v)9V+d6Zi23}Bq#O+UFc(-%@}VMUX-9hmjGBysza(zW#Yv}laJNA zuZ&LJ&f_tvtM)?bv2N&5cvZL4lj{(#hiy-Q!LP- z0H8Wyq$S1DPhI$@^}3J06B8iV=2$D=;IGRXTcaY~om-h8G2dhXMaDT) z(q|7l^Pti3QAe%gyBT_wyDJ-oTrl?0B$Ff{1iA!;D&vCiA#z?5%vIr|tIpnRUfy-K zsYOc?hMu(ELc>O5V@|45 z&}0Hd|DqH0lP>KMw{u$WLd;RF?sC;H#cf<-KJHCE*I&jnku6I3bd%*@=rWyJ@vVwc(u!I4ZJ}^_-s7c#S|I&GN4e)_X@naIi}vKK@w`;cy1}G2xS&m@9Zj)hIp?7 z|FSu~%QrCV*97D2RBekDZPWWiO?oX5v7MSN8vj_&w9UF0=q_uK&cbJF_~L2I7!?^I z;e4lr(0W^Ica*)q*@b3U#l-0;Xu19-OUpPl&PA&~yzIkt(7Er|v0Fv$=rQe>#8kO% zCx+{m?ohfFX6B2Yk{yKgqlA1*Iue}iR&R8+@^0Nnny;h^l8uqV1DqogJB(6GST=YV z*%!A!8qP;h*p9VB#aL1YxH9ADt+ka`;#c0u!R`UHM8WZ#;4K$qKEq8;>Yba5`#Bov)dK40s__FxYP`Jb4e(#0&Og`l3Sdsy7D)fb+owZsEKHY*^O7=St*}*Z(wGG zl7`Wpt1KRivoi(r3HLP`+XlX6%5^;fuTBzexID?9wPm7)Gn8nxSM?QC;)GUC`Ma)7 zT=0r`2(=GsH^aBdVffxWE8^S61(dU}#!niPJ0GGiX2NAX+1r{nA1b22IALnR`r~-SMWtju*_CmPXs#jjq}# zP9;nugBttJ--|oPWp98+-FBz3eSu z4x1)Q1ok-za!XC(=@j-~>JT`^uHYqt;Vdf0SE8_LSYM#&M(KgZdpjO=j;|WFEI>^( z(P-%&b8k1Ecz4+XHjkhktlwJ|=?usJao=ZNJA|^J$wax=6BPw$)gK# z3DMc}>5XA>d9-<07OBQE%O*f5ho8GD!NJ*EvZ$m`K{*(W9yOwZ_}iv?mVV->83la( z-hc$NG*&t7UtpFREsyGB{AFk74&Da1(0Mcr+`=eeBIUC0bm;6g68ZI1PcWE|e9)j4 zkIF_3S#DrQEObCG1Z)W>uM^llB(GT*_iIcsBQ%kYR-cOA7X)Hg{@0YDA<) z<1z+FQV`LMuv>0~-7;lI-T(ukQVay+i3e-AxT(Dx4q?!^eVN_O?`j-~W_W{Th@0;v z+w6aQdqA7XhrnWJ7mZtj%wXN;KFFz2c!T?Tn-4==_od@2POq~su-Z))IbU_f8Ur7j zpa?!w6~J%|>@&AW%_Lzn z%<+DM$cCLPr3S}_vPFp*A}0oNp=J0t)a^9;Ru8g42vG<;{*Is&O>vx%}G5 zD?YXln?3xkPD&{48fGGlTiVcRbn-|AchA^p*SqrA$a9P4?vQ?GbfZe(AUibdz*uY3 z9SCd?L}MZ@LW&CPVQgUJTbK*XOV4E3UJ*!TD`Y>ld_8bg2ePW~&v=^7iU=^!!u9}p zM(Z!Zn-?(eP(sy}1~$**Ydpg}iF1{f!nzx-@pZ*f)WdTcLu!;5_<(esGng<;l&r_L zZQHhO+qP}nw*8H5+qP|c?!PzNV8hNj>Zhwt)q9j=o}@o5lv^*8VW#1gbeT|7SNoRu z&Wd1g+B0lCTB)pmfC>rY`JmLWpOE3bvXV>v)|a061F6hq$wNc*k2+@4`whN06pvzO zu&X{s=$ThrBNwJ#NjPFeD{K5*=}!yLo z%q;SDnP}wM*w2Ze=V9(V?he+aL62zAKjG41I#Caq=zRA{E7z_-Om^1gQ3-nntut%D z1Y)O^@QtOh@2U||d_=(q>j+ogRw`A$gYOIzbM&JyVO;)#8(Et<0*&#q+jU*nH z*v(;wIy+d87jwK@EYpPqw2xo#$5#|#gM>a!Y`yWxaZ@eF1AamUNVfVziOKDDD$!f>yQVS&L@1IPB@A4Pk2+_r3_UN2WS>W zO1jqpmgE^T>?>fKt_A+I;)nsZ+bxgEs9>>0rD}pW=xoaFCK3yyrBhO_hA(5Ta%aM+Hib|042@g|4K%A5K+XEXran)mb6aa* z7dRTN;o#J)Dtd?xoXl*)%lBXn2Uwo=uh^?&HTuQ?ItWn8;p}d#TW?W$hbST|afj(# z%jf?q_WbVt7DTY#{;xut!(P8&eiAdc^&kDz_#LQWX%aR4ff+h>s-2LLPJPFPKajLw z#{4A0{Y(7M7~?QWj`ku3n~F-)O9-nM(Kk&D)g@1~2>8EZYaE+|nkG4el=d2OhUO~h zv=cZDpqev!ia^nND7wPqko zXIJji5$1wW=W4F`B7Podt?7<$jAn(NvEmUFglp|vQvCJ`X`XNK6#Bpb@k<<3?AGO1 zp$^43-++=-!h5_VYU=yqEKm42^HhEWhUgqM#rs>Xq*>;6*l&oj2bGmi( zFsPA}GI`Nl3GxHKV>|y9E_Ur&L|j)`cmka>=;u{A{h~68on=QSS25ICYkI-KH!3uanLP#Fk`7a-au{ucQP`l6OI)E`!d3pdrLfHvG&&BR!()ABL1{J-aMYcXo`5*jXS)7n6+2x~(whrw3L^3eLQNu{ zQ|qK=&7Q0Kv~rCG|DZcgOql%>2!!lzd#D{-$aLBEib5}(5%I$;PZtE1o5DK5XB#WG zI~&;YHZjAUU61*_W$A+KKBAesbKzp(?5F6##?X5`*E|Ji!Nhz3>3M`=7rVO?kqr9J z%P2TZ88UNc$O}TZ>u>oOaBae_^7gOF`6)}Yj#hDvo23ekn574S(wxFI!W3gZ%0ptmD-A^8eo{&ruI5 zAQLzM09jrD0D}KJ#P0;995~B>U&Q&MM$>XRnyvcf z=1@nC2RBdm=GQdrB#{y#E)pT8k|`u(x|@ocFkBr;Q4 zk`*15@_@S1^+XzN_Rd6_DTuxQU^~rKA#4a%Y+t9@_I_qxpr0g?rq`Qm%*GWMc{rvy*kHc(+PUMe_of~!dba~KQOVh<=h%W;&7sXiy=`l|6dQBWg) z(5X3fQefxy0)ieRdUl`{b2;5$c-M2-itYd29@V4~`oyn8jvsp2`vxJyV?`GLZbf^G z;RD;#+3%-po2p9Rr=5O!sVy*eBs!rv_}BN&j!|>kYb-9naCofb!J=~c3-aDs;j`0= zw4P>IwSmPc>Nz`5W2ZBL!Ya2Zh=(_DeVA-TYsd2^b`I}Q5uPS2h7V1Po7XinI5FdT z0-rozk?HTv(O<>Y0p#R_ zpZI5kU;NxTnRQgnP8+LJnfq>Cl~2?~9|o!r-jP^`6CBv0YC=i8pyA3X8u?9{am%^& zZX-348CvTt+8el*>%4WEDMzBxZYIAvaNRezGYeD$vmGrFQoFqf;w|hMtR4FdIBolF z47EuneU*};FB2tUo9hAjX>na`!zQbz{hY^!7y(op&-<=)b0hPCoCHDJt}dK z>Is)Ws-@?FRCeTd18IZ5^#htZjHSp+{klr`%_1Eo^mZ%YhC72r2?T9LmC-h`9+BP} z^8i>(NkuwX5UxASDr}Ms)|uBh->cwoJS3pYKH?(9^n-MoF#$GsQFPo21b1j%Xgd;g z?zA1$c=gD5%QIW}bU_ncd7?!tu^2)C5Zr=-^&ml!#}&l~^aI}ZZYz8^-VWs|+^Po) zcnO|)JM=$X-^76LSZlh)xmQ4nYB-}DK=zW{V*>An3RD}-i8iomuuJgy^y{j8 zF@jQ6js&TqhyL~0+wPRh%4_G@L(Vv>4c*27ce~gDbQm@h5kN?-vY|SB#4vMmNCKQ? zIcQ*W3ML!aaykSjopZv-Pn%+sftIP!ZD_oLdQ4SJx2@^mz0N$hL!=I*1UA5pxKCxg zL^{kCAV{kIC8e1MP$YPc#2ZHb(H zEj#UHto-q$|MWtFs+%vIDzS*Y&~Ho2-xnBwq@HmT<_8lk_$Rl#emfx*{w=l}8Re;i zpFKG`b8y=sYZ*`0i7XZHAaQmP;SDUTG`)&Rn}5y+hyLIYWL*XC67aj zd2O!yc}E{S>Z^TuhGjK8q${%FTP)PhJX_r(u5qoX5^AZE@9pQ3g8fC9jJSo2(?W5_ za6*g`$p{@F`el&iKSshHvSE_2wx(U>U=jj5k9v)czc}5mk=z(*bB|GEt#W<3NrMK+ zqd4=ptJx};p@TH~+dyuU@5)3mYFgM!;Q9DWSViB@Is?91C85Sk z>VaKgiZ9g8ji*AqI_Z!z0NnL4eX~vX687Cc(JBrL&;yZkLN`=Up%mxZNQ_Y2`*mh- z{oLof0sQhWOYzuw)o-`jWQ)NO2}%>N)p8wrMqdeATeQvB`XdBgDw7c~7^Yb~+{*My z>VV-m{>FY?p)wCk#v}w@8>e935(#$a&2=fRgT`Lhk|?LjSwiubh$y#^)@i7ddYWqy zw0`f7*uG&S&*Fva;w=XM;;XwZnY5LK z-US}k_}YrLo}IN31o9{ex_#)=Q;+m1hq3iGN%4`mQzet$IR=z*iA}mo-+#AO^!nn^ zt^bgL#ff(gwtB1|{>taq6=HhUx^2k^1xx5#eT#vF3QgT&th==$Z;g_3C#wf+;WH$b zF_-rV@ioqc-H}8l0Nmg?IT(t@Cv|y$MM#e?MFeNZ*R08Al4EFVVT|1ADF;u4x|N$H z$!&4aGJAwnH|eCuuam5>`I8_H?TOvDRr%CJ?W(}IMW`hR@H-Bv^|GRS*kEGV@sZme zZHI(tz3Chg#CwNpJp=Mm-r=BXa;|73?b;4_D6+@so{Eo}6FoMFBJBf#Q*UG)0=zdn zRhXh~+uqa2;}OO0aW*ak`a&IDMuS1muNt+4HXn1)(f}1^J^}hQ(Cn^?)Fg(?5oQ+$ zL?!g<1Irl~a}JF=XX4ID7t19~)A`gd-m_-1Ni2@4HcDI(>CbXwn7-V||Cow=JqQ$$Q7bMMEI4A{g}2J45ZV zw9s2Ke;{%%VcWpe86w$r0{u(sHD*y(xJ>^-4B)ALvZZ(Jvc63E$f@1(b&kILj82PP z05}&j%QCutMPY)52wEApVPt~awU2?y!6F^T@$;HIw=%ava8Hx=aeSorv4wz3N@Al{ zWS!!4oq=X;3|5UrguHoCi;)D3&Q7e<^`de)+kHf(*!T_Oe;PiCM;)uWa@<*JgG=J* zkKOlD(YqpaRN~FWg4|Eequ{FbK14OsG`Wah)-;){m>|elH#T0=>6h4ehq+-$dRE9jSB<4ozHKPSNlHqJXTshQ zf&}h;4Nks-#!}__8fY$W8g&K-&YM@BpDcP*N8iQmgE<(vjK|Zsu;T;wswDwoX$FWD zy=!*B?(=BA`F-$HN?YG7xXh<9VUywBOP`q9N)BBg_AObDz$MooGr#0$jKTxh0hk}N z1HO2H9fF$tMj_X)mlLoS@^n!_`!2aI&!yI9@CZj1CpTt4KyJ*QIl*`xX=19>YLtjy zcp{y6c8TJvPCh<-s|ht~Y6qZ45xz3nz!AoY8OzO{_ACU%g< zd?IBP|IVChzH_Fz2GdK*{S4Cb<>8_+h-(!K(@Eqg<>qC5eC4WJ-mD6b zH8aqxME})#P)X8PYBK~*?4HZF$bvbO+$DwDY&Hp8eqEe7 zb+Bi(srfY)uT0-J9W(#BBJ*W42(8Q!)u`CqavZ-7*bF-{>+EEVO3ane4H)fullt*h zrT(5}eBRl5X!%aU;>D8`VJl}bpiW(8Tf88F$p4^;<-_@8^zP2JFVOkLaj-Mb^|R)$ zH|~gW3(@WFjy$A!@Qwbz6)JJj&M6Nv06?`4008m-tx*4eJ<-}ZLU)LsUGE4Z;x`WSp`e@(w-#{&@n zK>Eq}Pqox25}-5CuXte}@#FpAWDl>0(pk&-^Sq|OReM%?6DRrXibj7?Tt0aWT>KbR z+dkW=!DeD+}5r*{QeWnD(TG478^VRr{X-gQaDw6WI6DAEetD>*^}&l&rVj76R0fh#AQ}X5ja1G!kkk;Wgl~0U(1%OCcVVy#7AIA z8VS}URJnR;`p+4t?h@8&R(i_Y79G2(s*GeWRaIjd-f$Ex7YkOmoewlhFe7~$%y}hu zLyJb}?UUZOk8E3~?@qmFa&0FZ{_!0p=g;3}YjQ$@Bs{dYI-rxPN>D$xMMfjP&uix+QzeC8{e@i(lYt5FL$-5~xOu7qh6Hx$F zQ!&c11ZX<^%&~X$e}YeLQhEz89q)0wE_?b)=Sk+Yw4MJ+;_Il@CE&)dU~B3{)+5VR zn)j51EZ%VXe^oWMmatd3gURqQQ>$4$y@-3GfjvpNkr*y&a~mldSDZgN$^&F)l{zOP zh~)*GQ4H2xqo!A`EwbuLVEcDkFFNceRW?)Bn=dHOUx{ou3O+X28qt?n$7fwCUEvAw zDrChN(`q+GEU{%JK~O|D5Bw{@e7_l-POM`t!idP0uAhjw8*XhmeHhjfXO#z$ zb0Rw1XWLM6H9ym|ck#NXe!Z>3leb~Q`95MOExJv;@5EWg5Eq~zo1SkqW zO>-mo#_sZe%PU8WaKv^^-v67-n zgHFngNb^}@L69kBr9K}9gdqv&rx-?u$b?!z^JZBDLDaoh1j^Dok~-o`6e`LtnZ!le zSF(Ugi)RID%*3h<>V61qil=9p#@(N@$0t5xxIV}z!+gtsGsdBV9o{IeRq!7fMCq18 zox5KfIqqM{6~S7-+c8accovP>qfNU<=$xVn->;THYnF}eFW*+%YEQ~T_t_>rdOq&n zP{6Kh`9*+Q+KdehlT{v4n7S3r&vB}=$%A-&#o)6Q;#>UN379I)#e5+3n5TeRbAU^* zsK)PEzfY@OW`W&#qXF(xCB)irCY>>L1KnlS%9kSZi zB%2f$>YLb&96f619o|V$#pz%xjbaXnGy)rRLu|;s25?YcGMI<7 zLH15{y7TB_b;FF{xV+g8pvAD1k4(BpJ^%_(C&lPM#!Zq5DyXs5D>-7w<;l<>==J4+ z5VD1Mn?96$92QU*zJIli+$J7X3_wbzBf7^{<)T7B18HMC!)7pT;2nfJuS7iD_MmV zK>@O6n1*@~3{!6+M^DCUhf^{r?gXAUCa4T`#@cLrCX(LZy{H7-Pe;l3gmPNIhdQ)# zZATkHrmGR^Cv^qP~Fb=^{euAJvzJlu1E@pMfwg zw(k{QV-vH#F07l?p#2;DECO+qp|K&_!}C;u=nldt4oM}RhQ0lIUtiv2_xH+BU@Q4k z8$E(2CyBc4P}+V)1a@|LyzdIvJgA+Ht44EY0fp+4`_RG%VHp_G@z{8 zt8i66Ey>YwD}k>d(wM8C4LH@T%qN34ogLhOyDB+!BG@x~6M7hl>&>|2)k=PAvz2QE z+AN1S45XmZHOuJN-J+M@gL}D4>847wMpWhXG*G%!g(Xd5^kp<7)#D9t>yetr9!=Tu zeXXr`LR?q4s6sN`R;sq<89)iQ(Nno*s`jeIs9&m)c;$BR1`5ksUDA&z0^an;-;^T6 z7uH&07SJqrM z6p{_*4irl?-_$ET)r9rragj8zd(c?ZR5rZWwP05rKbp|E=HxGAWX3Bq!O{+;N#Gz3 z3nSDV(Vos=2%x5dpY!-nnncm6z8HHDbChV(vfC$iGg~V)4^n6pq<|v~);-bGNK+SP zpb53CtZH2>Qi}(Zdl4%Hc~C``{W?!OE~(seh$Fpq*r4qq~ywtHovcL9UY#F zO7(QA%kdqg2sX8=0T_BA2!_Ji-G+XCT#8|_kn73=@4yy_Hx9>d;GaG`_o&?}2cxDo zft7H1V~k;{?F1=lW-n+x5am?e@yA=5tqAue(sRWHY{hU(P3P#73m|#c^O`$cSW=r~ z$yH9^$huLg>AIx9vk!^1Z|zcX>qZzb?VW?*lE0Z?Zw8?na4eKo;+l(h)QdZv2|TJF zf(-@Nw5D)$&CKY*PP8|_`M@TcMqJWKh9L=7Ek)pi2457g*yL{4wu7XQ@%*U{+@4X9J#;yX&5WzN}7Qu1KPw7{xlRQ?rBGe z>bj*)(Aj}}P-qY_Q}=zO3oq=?Hk(VRDd!WJb_9>_sA36r4=SOmXXs5wJ5{h2Ks6P_ zR&zHE1yuXF?H8EzI<4oh>E0HjO#p}X8fk<${0+={XU}YvFL$Zja3Qz-LHIsB%=SDA z2ex~RSV_987i5qA= zmq+s>s$nA>EZUj3<9omyy?^F)gi{!U%YmHGKTTIzV`YeE6!RW-C@A1vY&g*H8^hD) zE>gxcq95k@fp|0|TszQX;1h$WHmDSab;M-VS}0sM^i{KToF)bU&`z@ZXw~ervUeMs zf$MdyH-Q^}2WU2#_u??n9(4;w#|l#JLvDl zE-u>ld-x)^&->+L_*uK_(nEqsB=7sy1&hOl;hlA&@gpq$oulDt12$AW$qrz%Y7(Q2 z=tOKQdKncv6I^cx!-1ST8G)IMa@uNbnY|{AYdA%vSdLqD?P4uTC>y0GN^KpMF#Jd4 zWpobxi1-#6*jiSUuCsCr-bvhsC9B|6qk!B%Jp$y^57Iy_$yT3bQ(XuqANEy^YW$U6 z7bA-RT!p|AluDe$+XZ#)zflt&2ld5A>gXQd9K!47d~}%d9*6u&=6$@8bUzX=Q){KrlCl7GNzg`7 z2`UU2uYCbK@e>$DVRR~f*my<-&fOcm2welxe*(mEi}KU=V)a?2c^)#&l7}Q=g04txjhlFz z=gE0_V$v#g24`Q7Mh=@H$syL`9q9gRF&Ws~>ID6L*Br{PoQ6I6G!xD-J zv`hmMhBn58ba+JKRIO9cm zbkOb>nog*uS&dQYDCea;ml%_YSyo{16VD6{IfP`=W1RZxdj1$j6V$7Y*VUPAK{X=VWE2HQa1rnY<_|`zeyE`roJPu^mQ!1TSdR z=@I>HNkv~6UxxW-&D~Swq3gYJ(++<74ecv{ z-8MG@K3`K2A7mR9Et^|0)29nsM)f1w`0X+`qx#yo%-h4`UC|B_0dvGc>{A*=&+(Vq zO5-_Y7>|f!1BAd;K|KF)E7AoNlBiJg?Ebk?9PdLIr--t+n&f*rlofx+vxYlgJl|^C zZMlRvf+Gsw9mZd#_x!~tHpYP8{u=$Avuv6E?Mc%pRb;SDUUx)4LAp%E_f1g?AvS@h zb+k?P9+}p*ji(9*ptXvrXB5#~)AwPovdZ^i?VAB>W$u)#)V1q*8JzV-*Z1e(XY5JQ zrj6P_7~<#nlL1{g*qza-qMaJ#%uQ8~FGTS~kFVp79NCU`ot_3kSX_+n$W+rd<6-Z{ z_8JbHt+_?$so~UoARt z?nTr#0uv6F7mg4t;6T5&nC7L?Fv@-k$LE7#c0dFEV;ZFN-AnQ1c7D7w^g18jwf*&h zHz%(j^&EN(w}KmOcd8Nc1<%4>xc%-o2XC!Us63C~LFV6{8ZJlE7C_{Kt+v4;*R@=) zOy4<|OIX>pJjx4glGj|T6kh2)^T-~SV9l=_+%M?=Hs30g;s6)~jyL&MJ8b69%jw00ZD@vXh7Z^JT`7;jwtx ze+mCf5lse&|3VAg+1}->Z^PLQ>tD@~N3T@Sg|6Ap-Jgaxw|AHQe!jaJ|KvA<9@!Je zLnA>PR;*dmM>78G4@nD|qfx0QQVbKd_KYudC-x5b!g3&6>?ld3dD@j6+6K{s?i!TY zyrh|@B8vI4`sRLo(6oFb2i#H|>d+sues_RgA@`JYE#TdpqX>Vo2OGQ)fN(bkU62FK zZYvzRCE6UA9@9%x+zZ~`13y;Ug}Cir{OGUo*d?!7B>Uv|!^S94 zL8#u>0)mPb<*SN^PF z+=71;Pe1}U02L9uhdVv;hu-{2{y;2|{DYtfN7LUdaf1dFYd^vP?SuaEhm7BDKq7Fu z_`4(hK)6@4(mNgLI~|q$cN&B=#teWk1gPT559OXO`{n4g!6b2J{UZ4Wh>p6Csu@i! z>BuO}0WKo#$M9JFb}_*<4g8Qr0P4{ml;%atleawksl#n{8%|yaFZ-vhj#%P+MwQmE zINx`yeC-Xd7h7n*3wA6)ki9=XyUy_bm$<*%{6FiZl!yf}h|3czPnKpAzi%^>Rz z3e7seyu0K4DdzX}>!gVGV?59x;4jbfAi{zh?E-1JM7xm)yf-2(%u&TP94Cx-bJ@URd=2=z4uGo~PoeuYp+NsD z!*zfl=#n*}wHu-;4v=kk9SU~8|MGHBE*xLWFbYct5J-zez+E=80k!oxG)3NZoGEwT zh~TnRVEcDNzd+Z|b|GGV#nYKt@jPl00B|NhK?SGY&U|zkwEZP@1#A~pkLnzx!b7C* zv33#m)viA8@C1g*vP+=Ib$#Jn4~ScX=sD>@RLG_YVl0Mx*Z^+?vptX7$nK8So|^z6 zMpOAeT@qFgGBDF~crs*y9`ZTJBk=DKuNQ=W2Tj5DKOnEie#8<6c^geZ7l=A(jf_>w>Wf>+`SKDMFFII<&DApx|7^ zkQ}Zk->+_)aS;N?{UwX+^5)nJJ`-c#I9)v~UlF zq+gItxT}j2((YPl%vH+>=};TzB4KoP@thWcRR>&(#v??#b^MjB3CCe*ut)ZK&e#afYGf-9VDpNWJj-@>pJW*UTQF_{(wpipb@)2 zShF~uQIs6br0q|Vcd#9!@B`LZB-B{)WLGX5E;eO`IwGt=%1o_nf#JEL%NtoI&@1tg zgQK`NC=Ro<;eB7VHc43PE>8^7ZiRl(okfDu9<5chNO-pt+naHF^8Hs2_)u;G9W)C7 z?_@l7cQCYC^B%j79|*NMDNeFrCIsKUd$Zfjij*SF3LN;2#f9F8iPBx&*uGO-NP#^g`#Or9Q0k0z3pB79Cbcj7VZ;+TI4zs+n z;c#7O9V3G^fTOgt`b^B%a3!!*C|R+D6Y@Z+qOm7?*U#lvD4!Bc>#3jhwo0-_?dQtJ{dcHE=>Xj=;gf%nFVp8-GY9h7cWyXc ze(7*hMAMKqn4o^M#xbeTbETE#Lxu4vK0q-A``~p zpQm~Jdrm5uvs{4&#C2u)kse}=Ec0Oo2A@IbOpiE-HVvC#s)i*)EKW|qIb{v7D7c#2 zwt4OntT&tw%Xs$%m)-e24$0&XW@ant*vo!b^lOt>dwiyLJoD_k6|GjA2#htk9gJ#H z!!w9@H?#FP49Bs>vkge`oC!q`Yrwk$994C_om2Ou`yM~v^7|}LYQx7FTL!D5BRqhO za5)MxRG4@JxK|Dy{9B&)^+Z3f^Es})ycyR`qw&eMW*6cP)%NRa7R=5Ow~lr5BujL8 zY3FeEqx$zAF`jS}^|_TlO9x(mgo~f!*Uj~=0T|_wz7F>0OI>n=sAZ5){SYk8(lgfs zu?+aua#t+5d(dw1+WROWwcs24RmGv{>l_C8EKd&(J~nncv4JR8ls#uyJ8F?pUxm17 zb6pkL_WZkjZ=*>it4UMzmc`%`y~khgh2BfNw6ckl`M&*Xlf$@yz^9e= zCc2d`*hj8lJr6h6RZt(gZWu=+p{GE``y&V8x%l^F;Ol5t6wn1k z0DKt_oRUe7$V2EXoK4;O+oJM%oaC!3oAc1taoYfWIP;b0tsi?XZXV1@5suK&4N}3S z40y8Y%`+&w1Ue(X_5@7N+)9TyrF}x2enPcLyFoVq>t!3|Qu`GF+8@>($ts&n!(-;B zOc&)}2elUZ)Uv2z89ktdY1O(&uvcYOl2}H%4l5&^*doE7j8$(A?neM=o7eyvT$BR( z!YU5{Y*L3xsJOgiyW|o24hW!4i)<}1E~sD^gbXYco2alMzK`h-P?-tNhJo{Y4CIn0 zLQoLMro-4LEFu0_3AMHP7Z#r28m6-;_$UQ67mio%uk^fgE*S;=jK@Zu9@LPQ#Z_Q; zluyz)C?M%sM>E-ZIE99(P3q{>A`_}2w_e(KH3KE{`G<^Qn7$d-q~DSJ!iRpAYPp{l zsDz_csc%i#XCG09NR@}gP~5!{&$b7IfUW0!2Nyo3{Aq0l9F`-D2{xDIAuiuL8iBm>C{hy?_L`#OhsqXtUV1N%~msTBPeKI}@!TD8S?K2+);M}n? zKq;NzHed&>z-Wv>^s#P?fUUxr%_b!`*Pj(PbtFwX4S9NPzC+aUmg+%*W~T_9WnYp_ z1=qJd{+oK+WMx`@46-<`;sA`)$mi#Xhe93+x4sWZS)>WcWnB{SpGK45u59Z65L z%R8Yf&PW^cA^0xbL9dsNTuvE4$mKd3U26g3T^aR%Al{p0^862;LJNX#)Uu$yLblP} zx{GvbScAHw%>i}CO9LD@9&#o|#)Jtv7Agdv78&2>1RL!&FfP-rU)iqW*^Xr3&0V2# z4d?*NZ#O%W%c}7y0j8}Ev4p(au1z%Uo>YE&04V2Dfb`2wNn8NXBlKQ%l$V@7t8)+7 zh5Nd-dI$%%yJzK4UlsDjPz?3b)`_5?htLO;+qjKr;~wfP{Rx6)66XoHWizSMJ;l3k z;lFm9_e(}5u!6B)AEc1MQBy7fJY6-Qm@f>xt~B4_UX{5_;re)59|<`!9l(_L=mQLy zK}4V4#>9b@itg3o%RO`Ci(EY2RqcDLfb!BYKP)0{Pm;WidGm#&3oh(GQDVDWD1cwQ z!~=M;wO{2*@&4Dkr`e4L>4VT`Gk17BUU#SG4hd*$2a2JN%lpGaqdHMIXeeS@-(*PO zjBs;ua`19fb5TqoULOT9O*LRXBYd0VX3vGH#(}MsS-N0rYg(c9Q%Ia#5M%wSf({p% z?r>S(f2RQw!Q8Dyg_h)D2i0u29wTKpy!}oV4K44O$-fUoN9E?$;cGjMz3iPsu~29E z+@{Ilh;=I?wT5w;LY?{%%@knfyWa$gXJW8p&O( zbt|nGv|EzLmF4xV61tY?I&SiFrMzV(XDP3lR$eTTv>@Tfiga-tn#irF(!snE>@X6? z`N4;g*0<(|&X`x>adq8_Tc1YFBf(7wc&hw->OB4dR(_B?hUhJMQ^7=j{fqHlS&*>mZ$ z=fJI!#h^im8WasF!t`ZEQOQxZT)~{>CUeH?;219-iOLP$wsY`RadjSl$4pCNg40Do zfV==sWhmg97z4lFf6w1m21|_YqJIsWeLD4IUyU1r?~0oRl48S`8sY=%S|iZbr^RI(yC<+_3mf>R=>|FfTTUos)U|wm8k>(C8;hLwl{eg5uvQ~mgJaPjkh4)y%_IB)ZReKZ*Xglho*C1-%xcV>kOy5wJum4OcB8C$M-N=qmO zP~@~i?^DBbpbm0eqRY$3h7fjp(J=JoiS*0-L6I1_;6aoR+<+W(iXQgs8K$?a%>Rt# z@PX(numpeoHhC|9IRJcJonDzKFJ^J;bo_@+f)6uWIZ9l5W|vsHtN(Ut`kkYb8byomZ z=V%CeflD+R!~PPL=s1|`?L`(SgZt+Q8uQTIxyxR7fhgZ-3_j!+YU{Um8?;d;HWOQV zhRLfwJUfUKP6#{NaYTRQaM4yG6&yF}+ZDig;v=+?OT93OJ4D_yAa5>C8ab^q&bHP% z;`Jb$n+~|)<6m^dkuJ3NmVXRIWo4=!rcb^^r;!MZ0O)euF0pVmLqfRJQ+vA9kLhW- zYDI+`U<&zX%PmneCmVD4N)tJkIwZ%t#EnP|>d`Km8x+T2w=jF`zdHctSq<@NWj%AI}JfV!fVbHzmJ<3BJce;($i?N{yNTz`MmanyL8x{qwYS z!;ZPZHAK-}eT)~K<8|>|oO)_!a=wc(tc_Mkf4JD5%$7Z_8`wct<$YWKGdN!-fx#Tz zqoy)}0r$!2XB$=VjHWXH{6ogrFbxM=RPG0)-lFgLZ>Nxhpyw+Ex zM!4Yxf~Ro%UHRs)xJl8iaXcQv6&<}Z@LkbVYFuQ$q(}SUhBM49?O6Kro8A)dima0{ zM6jw#i_G~|97*jalBR9&P+;|`LQzsa4d z496HEfE5Q+5hyDvn_1Od(jpsD1 z$GS=L_JEtG=v~vhL*+SMehN#duoB36DrJ7nPnTaaH=3%jYwgpgB9A%HjI*d*d4Kx^ zz^}9S@%Hw(8t)F^2o|-iUPV*x7AqF89%-KZb{F|M zof1{Zv>Z;{w)ym`78$iP?uVfHw7)X|Oye9_`ezeu*c*E1PBk<1<{;yCSz8~R+-Of0BC77kDs6Rgd4 zbN-rVeNc8-A!UUAY}37#<3HY?GF;%E;X>~j;Wv_>!}_w#P(~yZXrrQ=i61Pwdw(QN zqSRPOPT*$L$3v$<5d&i=h$KW84D;Zkpae2zPz$Dg9t<1p0jh=HJBjWzWk|BA;N_6x zICj{-X%iUfk4BE1v zdDin&(Buf8_OEgnF?*imGRY>I^Fad#o^C(&CYm%&1Y?=3E+w?=Bwp5#fGh!tQopWb zhoR6hZ5>Bbu^jZr35{ma2mtm#&+(txDUrZQHhO+qPY4 z+qP}ncAo0JZ~EI~^cj7#en704G5x$zys_FBUCXo|`<5c(^x^UE{j7wTeZO3~_j;la zt;tV*KVgjMjv&aT@H?-1v4-hWSX&kDe{+x^8H02BUn1MtfGCqNz={B@xC^Wj690^y z?G?0i!L1RRpb=47BpCM5)A|g4J5zK;h;_r8#3=p+R*DpIPpEE@4jDvD7l9w!l%Hoa zaFn5^Bg}{F1ZVIVomV;p%lh|&V+&2nT%*tM4JR37L zXKJAl6hW_#Ls9LM)JHF3GCHr>;*i;5UU!&HZx*%r-hRkB%=Ki33ojj(XNYd-qDa| zdFMXqjy*V&`5R-TV;F(Q1w+Hi$eLqtL||d4%s*_=_RId}EHBhbDX@26U?CbT%Sxjy z#9Li0!ty6C9ED2Nfbl^9yDxtb&b0eLwWV+)9M06}n2aWZFDmUUTTm-2c8IQaCvCQv z3TSJsA709iJhWY6d68CpRVSJvUab2|S-_3p1Wvf5&NfB~K~=%I7}Jb;OZvIiY4#uR z8DZy~Q)*s7K^kfpV*+3E?ZJR!PdZ?xfN|(hRr+oS2MbE)(4m?p127xfAOCo_^}tn& zqB}*d!<;eDe%#8F5_S{gg#t?U!EE_?EWKA@MLWGt65>PA#e>b;;RC}eh{ZCBC9uSR z8;k~~sb&ti1bW$tWxXRPI<+BDSmuV!hVk#2T=zVb|C1thF|9&;3R;|?lVfld5FWV> zRV_^rIkjqGzzl!_!rj}y{X6-Z3U9UN!F2e^3&zwzX}z^0@n&J8zG$y;R$mJ56zqldR2K8%O>3AsF@#;&NV3hnb>{+uv|Q=!GE zn&&5Lu5|rNlj^RCyW)3d2huO%Ov)ER_lHeam3k_-bRwLioGfTVrk3P#;S>&lPL-*C z;P-kq>aq?kfng67clwm>1M?-b0=kBp!WK6tfHq2kA3Xnh|=7yw7{tfLo3&oxatJ42}%8!4{6b5^2HRE0m(-2 zOAVa+s{6XXyeuv(mS;NT;+ytgFJ*=%uugy0(;=j%wJ0i(xn4O z={QA}f}tbqHk{orvdiw-=`gAX??l(b>|X}c(fm%N-iKUI*2 z|8v2e8tvJiy;eS{ZWebR%fCf4s$JA zvTH%IEUkL^hySQCpw`W{!cc1l-fx0sg>2_dBXm~13SAbr$~6gSAlDaud}J_d&xA3; zr%vmO{}=*9qkSqILzvj&Ah9ta!+#GQZ!UuFS#N5y3dDdYeOcm1fT2@9C|6liohH_90Gu@}z(hoCzTT)ei$>uqCXp>dLMTLp02Jb<=ufx zena|h6Z6E+t6Wb;Xk1bmhxE1CP{nCDEQo3151Um;qpEsUI*~G2%j%eo#z#~&=H};= z-g5YcX8rTT2LhN=A1=J$f{|ejW(&WZoG2UV3aYXg2gs5S%UvM946h7&YP{G##iS6p zs3u;}Z<1%*=IV@Y7WE*}3c3nxPLlfTyef|DOIL|k<8X>FgiA7=Y(q>)(@lp{5)VwI zuEOiFyuX;3R@#bN*d!N~zb7QHxyWr9Ru?Y)G0XdM%Y)RmrR4WuuhZb8~yPeG;iXbn@=?2 zLsXxIO4hr!O$nMlST-E`Vl#LiHMUvKYJW1TF@__eLBUqj)u!^KPCEC$t%Xc7&2}c3 zgNl38qxozTyP)#CAif8Bx;Q}wVO|~(WM`><-DnbuZUY3c^+A`HET>^h^uT(s>}JqcDdv+I}cwcKH>4 z=nbmej0>+&Kq?d=qVFBoz9fMA*G_#HtC=8kMyOO{8GoDaJSZ?m@+mvBL{4?w|tk51u`(&QE=JqNlWi^j}95mH2 zO{P<@TPh=#A_iofSNsWbh0+yg{*5wfG{81IIvv29-}*%DCA;3HU4@vc-3=Wbp#u;n zU%o(Z1DFOw_0Q<~-vQ!c!+Hhk8^A)VCenVC!7`%>N?^x!(1~6+4-)VNU0b`9AK@7q z)iEHcXicNaO_{au)5?wakV@Aw%CAxz!*sV!78s#XQzfGoUlOO3o^?*%Oi%sD=M`Xd zUa$2vDLY$;CM_`$UfrS!%6wt5=U5-$0Gk`u)zb#qvlO!yTn@J3%2M2TfB)$a}*RGEOWkz(r;$QwP zmtoT|T4Y!Sov7nl-Yo9J8@g{LZiu!oZ;rDZYaO7}GdXM?_@NnLBk+T1u*-U&d> z7I`9W*8aJ43fpZXtz8#k&a5$%qxuUChEP0UunJF>Go*_^_92XQ)zkH zh82=2L!vX$myW+eBKEp%@jq(ycQUZY4QKGGsbTm2-(He_f z*amAq+B=4n3>4JWl$dcIf*i?3UDJBNjy8C>p-CuO`rDkm^5^}WRup;A)ZW=X;Ikk7 z?@+;^)g~v-WsVW)xgJK-(M_fLlwshQTjA0Y)g}Lk^>`I3v-U#+ka~a!i0;YjpNLJI zvxmfB+xdKIa6CF>5iN0-@^@XVH;JOx%l-Rh5>cxs-|IiRu=EtJIdn3!mqe}TQX{oe zu)&x;Oci;Hpyw=BN!S8*zb%yQ^%3KApeg(~^{khK#q=0v<(BX;!IXanl4k}X%A9el z#v!3DQDT)j{rUZ~IA0zNsUPgw@%Gy#S98EiIZaudz$B7~08I@emr9;Hl&Y146eJx? zdx{m4It^C^IMJO7zG?w?_JdymCo5ea(nUHnT-y}IdkYr-9_-O{1hq%CMh>(K)hBl; ziK84b411EtCT?mhn7kXYZ2HSJ1y+lEL*~vU40>((^~R#6fK+}6G%>>J$$FE|>B7N* zeGkg`uPAlT$7^Q+N&GD%JY}ZZc#TTfN&MiJq7+C&%#rmD`N!B>rh&JhB2Hy&%dr5X4YvC- zjT-9r>36g6>u~R&vI_L6b6AuO7ki0?6BsVS1z<#Kz3O%)&!ql+&#bdFaUf2etw|I$QPOu0? zlQlXGVA?X6y^Tw!!Lp|#z7TS}7K{Mn6{rx3aA^LqLH71IQs$L&{IwDO5G@=nuN@5z zRcnQd);i~3OI7+wsm_wMZQ&kCV+y1O1>s@z?Nza&(aRjYE~5j<-}xn3uu)G5Q@$}V$_Tg*g@J95)WCH3Ny$9P&Z&S zsRQ=YP(Mo^xXkEL7jAtG#L~3(H`CB5=5p%TXzq&Q^-ZV(eTP&Jh`u&~zl1^mx(~_E*kDnCj^hx>`iev4X zLf7>;C!W51JjE+wOBilO7UsN^X}RxI-1~jx{xHW?}z*A=V3o$N9R8l1s8D?$xZ%=GBvE3UIK{~fJQ8C zwaP`=dhC6Ma16pVr6auqipPp@h4Hz%3jyedl$=d6Dm(A{6(&X`0!11M$HZrPtxNLzENWY_TcW6vlD4w`s)(T!jx5ersm;!_2hYZS$S z2XH4(GPJjAQEcS2(y~r54K2T#BtzG|E9b7Cx0o4?w>v(q^KRT}(i%xDV3zw(QHn9# zMi1SBC0{bXNGg@kMkzcb+DfPvR}usYZ-uY3mAn8cv8Qv;|N)cD2?l9 z1fp$=VT&egM1$z`<{jez+PEUn6Ph?KET*NK2;cg@AMU1;^do#7&=56f%j5&dO);$< zBP-a?^k5!k4u1jubH0(dMP?om5CGs52mpZQf1Yo&ceJpvaJFzYaiY`Hv#_;r*3(SWFF8n$5_RZIv za&yvGP>wmtSfHHR|Buvk)^i*x-V`T_DlWw<{5nF8!U@;P=;qL{2q#s{?v30r#J<6m z*b&|6+YPKp6odQ5{oV?$xs(&$#RHLeP)?tlcX&@lw<7a{W7>O7HzI3nsUFct7T?Tr zgdfj(aJV;xP8ecTat7o1W9Ag>7=P|@kA)i-OCf&9OX5XS_d@ zEzc`8a4t_7_SbNxX8r#Wkc||?n6d!{0JuT~0HFL|ovneRqk+f2gY8zcu{&T#_?fA_ z`-=zgC+5tT{d`W5=36JITmRI$j7ICZcXv&8jH;P{CpuFPsd9qHGz_DlmG}I(<6yG9yzuJ#RO1X$Im$wG~r%K6W}6_ie;u#Tb)!wH!m)UWBb&u#00zu z*_W1L>I$t;9IM;SJOU>t$CZQ_JXK4YDsWn_AQsv~5dir>?g}#7GnPm?u~Qmb4hs*e zK~|F;mTwYZ7G`R4WOmS=L_u<1bc;IOc#+@FaSl!6FqnXoT1Xe5!(t_l7aZiLWI9hu z5EOdMp{*9iX8X7hFq<3`Sr!+D_DroT-|7q%^w)FKutR%mJ0oLVO$_cQ}hV_aQITUx62Z`T*k&IxmoB}ar!_~9s2BQaSOT*`@08AdvswU#p@zN(?`=OCP-_yfr*$-2|a9|E{Cn$vf6Sx)Jh~&{0y#l z%Y?^2)1m<=2Uw9P=6D}xK6Lq-p9|LDRLX8Tn`E^t!kK>XAT}wX^boIT*EkU)C>=q+9ip__Y&zJ= ztbT3awlt7MW2&xb{HX)J04O5C;235^4fIuc$hqR{>$V!)Cb}1|PA0c2RX)OPW#JJ& z8CcgTn1XlGu@zfNEaa4U6TpY}*3oEcb5wil9al~_;;WD-7&3WBS9(_GZrGM~xp{a% zw2V$u$@{Q^Si4_MX--L%K6mU$mAq&lC^beD+gKZIZrpDsS{bGq4F+C`T#}1dd}_BV zHEewe>l;>k4QvFCiN;639t{kByF5t~^m0bD&ggTsb$yyC-W!eUGD%q1it41ngEq6z zHE+OVPHkguQdkBTp$uaE_3eidy&FI4cItAi?m zdrwXF?liJ3jsRszeybn<-U?ar{VN_5XV=*UIu?KQHoaN&p;uNc? zW15l)Mnf@y|6s{TTjPl86d(`*)lg;bNJux~uhbNW4R$JsC=IOaI~{V6ZWq;%V%}vc zw9mo@)8`%Zq0iVp)E0wtohD(CEDU*yhu4ElJp`M-#Cohkqa<=H;2s%%9^AK}PUf?% zpn)v%(4)nwR0qArUu!ZNBi92^h{vk(XO{Jg0bf5}-h1$Le=Iq@=_MH7R!72o_v_Um z6QDN}rf5S5B-q#GcP}?(WGb-OOcS>Y;|u*(NR0+>{bp|O4sv=sB$PWr(jy)(2j7p` z!vG{&rY6HTeMPMDd|{(aIL@$7*_emMVUzek3Ryqyv;f=fc@=XD9k=wT#AN3STekXR z@+Cneo(2T)V?I_xM3rcejlmISu(!Og zH@F{zd|WGU(d;WmHX5v0QSlU@uiTSM6lFX)?kC0-P0miD{}8MRbt%E!{?h!Qp2N)H zcx(jl4Aq2~06inK>H#?etvn9g0>LW6aKs*VBZzor8 z_Kf2tLE5@QbS8NKS#V^&e!*S~;A;4YF|<;lCg9waLn=sAf`oVXESg&XLD|v}>`}w4 zZ^f*GVh8Nk11tIb>A5_#EeEQj^DP+d&~mds##T4kh4t?Jq-K^n#Aet~`HZyPrMWkg zlhItRhxk=DUm)i4)%_hkaj$KY7cEieXOy5BIDseu->kN%elcCp=uNoyCVaI=mr#7l zF|r4~(q%G?UD)|?s2W-gCxUG{N)f7u?#%H$p8~3d_n4V3RcAQ1@0Fkjhd_gN0}^|f zp%Wz*DB@7FcC^Fq$myIoZDyslRGhrnn!WvVrTWOV4edbv(mIy8?IIVStSXJl@lc+> z2fkRnux_-b-u$R!GKuZac1|rEvri}F{wj>U3ABUSWMe- z!q9i@+~@#FaxMeIn>pHYNDIu&yw=~)faWix;TNOtMC(1Q>+Cgqj%6=WHc&X{WwaO9 zLv)(MCK5{l8j0qtfK*&D;6;LO14~>*wlT^+dp^wVQ>VeV2CcBpgS;pGBbrLZG81;j z@LzSAF3$$VVwJi>Z@hLd$)t146i5(1xuNXSR>TpoJ(aPxk^;4|qQ5c6t&Z#XpB1w! zb3)Qtd;$N{uH!UYK|(B2R;;p|+An5{|H>C`sJ=iKEc0e4zH}d41P& zeTgFf>7eOUqKrUe1Y!FYF{Vfw zY_CIp3y8{0+U5Hqb<~I0a7R6Pah9$-3z31WLkGd*Ng>mP&;{wk0H0v~ zh{1RRONrVVHh!EgnMUcyWEU6Ct#0E6CSr?v204%K-U1-;o6R6$gVhJ7y}tb2`LgQT zi0tN7-D%qEkpv1^C{?}B3qg{n>eo*G~WxogRr+SY$IQnQX*&o9fdcOot z1xFY;(||<1L% zD;qQq1EHcbUZ>c7!wv)&fl@S;jJu$Z74m?5xOEZW_46|X%?M_US>#9PL1gfC%B=fI zecEG1L9EUhx8V0cfC2X3b$iN?lth}RAimld8Xbb%P~oHZDTuY~!qk!)79Qw#VI_=( zV0=sjqA_w0O@6xqvhoSWpZ3c7-Sl7oQ)3Y`_;mrud zO%4ccAV1pT8h5Sn9Ix%g4G8R0;bIIu)S>|=I`T<<7UWvhHcoo^^y~7$WMAqBZixPI z86PDtYl?K7+vTih_eTHS!4M0=X20t|@kJYW`-5J`z3q*F>8xNqEHBNW3CTOrm}GR+ zT_&h#5^Fi`1+qqIHcRydm|1CHsxPvtyS>j;)xR?DR_!f1=Pnk0wsB)_6^&gZ%&xaA zKbHpcdSFzH{Sh9TVFo!BjyFTrDQ=nHCnteOWbTE@?TqT^7keqbd+I1Nd+z2VBU(6R zJC3yyJu9=8wWMN`YT(`4o(b6RouCfJ@Z{-)=0s$4zSVbE^QeRP?1*Cx7S^6W>OdV> zm@t9Wgft!Dy)=Sq*R?H3K|+d&`uL7V647+cP@KMF8&wp3sq8Z*Cf7+rcHdvyQ# zo_PjP_MiPLPa*z$)BblQ6h{MFGn0R>nMc zGW?$sjCEcztQKP8c4q9)8?JHL?BZ(Nt0eE=&fVWCXm|BpZrPqFppxP72H|sIlDVlp z%}2wiB#O>xTK$@*negeA{wK!|%mtbO_$Eog2s(AD?RYqx2hxgYOZ>@-m1eL;7~!H6 zhrSZ!SgwNNn8Kf_>!x_@J+y`LMTU`JXY(cj&y%eKT|6;Q}>21;K38V(RZ^UE{~j;jc|c7cS^=ua%M2zal+h z-sX?Z3O#Zts4T5$zK-qvTq;`l25q-AvG76htXHtx?nE%=a;mOj zXgq)iW4itF$6dqAwdmHDFQL{(#9Bp`*?X4(q>BLG6vHpumK#~MvPvdf`2ElOk^ogb zwg3qLVDN8co9ch%|D2p1|L@B7F_w+pChJG1A84m{owrj{u^Tq45k;oe;*eWrVZ-k{nH8L(8q|CUrzJGg-7Jr0bJvBNJG5*ox z!O5Q_ioREW%ipB3U^0s?<3mdwVMx$SLaf?R;wWJC*N~$q7s$lX$Z=uDH@-3oDtsoJVwtA1SN47%GqsTM94{3V1hRIC=?VnewB)G;kdsuAz(D0u7Fs7oY)(DZ+OCW@PV zm;p?v$EI?=U#~M>FIF4XdS&Q*GtQXgASCnJiX5$deHN18!F}GfMWd2F>juIEo zbLR_B@x_i0g7j{qLJQnralEH;M~?tQzfsu`;Pklw@O{pHE-EZ2`z1N`rUe-o)&*WY~meiX; z7ZgP=A*GS?FU%Kp`=X%Gz{9 z-y6*d9wkPX0R`lftht27&ezE##@8<6Q7UX36~(q-l`EbT#99Vu0M8?-NJy&hGLTtn zCcZB9XEN4JWi&H5Hxc9=3>g%S%P-ik|CD<*U&99uEV^@PcPYGm%SiTLixTM*<Gpd_E@NY2Sx0%hn~BqGclK&7J{pOy!Bbtpy1Sk8nlj2?1R7t8fZ4Sf!a)A7f; zBiESRNDNMo6EDzCj4sSbi|{biszofTA)F~q#U>qmo%o3Ih(RhQumA{;_m6p{kZL{+ z`le6ajn{UK8HajYx)!0!2l!@O;HYZfIkNq8LZn*P6{1CaSdTzyl#p;UPo8xJGaJIv zX~O4sT#DePv4YKtiY%T6iWsHC5tUpqF07B91Ij&qTDX9dQ@0gOtW~iblh5 zeH&qNK;3qMDElU`XrUjgIFto z6;DfeM1e{@y|YSLoh-E)@x};P?kvtQw}aCZO1UGh|}I*!8^9h zD#(3~n`AC-QEHIz!3N_i)K_U?fczxAqW77B`kYsFy4Ovq-QXaWWQ=9J2hicTsT91Y z2$CyC+(0<-!IJS+n)Tcm27jaic=E)UtpOv93Q2|AFvhXBuw>=}CFP2qQ>2KHn491AnI4gaS5TX_u+IFLk*Q2Xr$_VOA8cqg*uz1v6QyjHNVjs+UZuHq=5 zQNlGPKa1|nf48}H&%a0)6?8i!$cw30V5t?$hnR`<|HU2h+9JnNW3hX-YaoaIb0a+uP$C* z(zd_NsLuj6&-Z>ISAG>ia(i}{v6`rBi3GdK9D zQ4|L#)5vm605NwOGyC{^ugu{_QA}y`Z%N#%)%bl4{~8}TS*^dEwPen*asQNp#7m91 zuPMkqX6Sk+JWJED3h&x-RUc|~gNr3*X}2(hzx&#e-fJwPGg`*3q=P2*`0EXdL(jes z3dFo@2(u^w??W)+eUsD`c^Ul(&-aYYe(xStQ_B_rA$AaQ6WEbI@ih~$FGT^w338uh zhdhQfSDIsAfs+rK4W>$1|9O-)t*SSn#r$q^i(Hobggys0;VRuSa!z#!YB?!ONC~s6 z%bJtO!Wnp#yWxXSGZyeLzp&$3BCT$j2iD6go<(oTzC0LEBi!@F#D#U9WqMYzQ~c){TugbE8xA>Wb}t6U~c^)}%vdqa1KZfITTbG>MG~ zfbxAI4+jH(BZM}tW)6T$4Q2nG69`?;h~V+(D$Mmc(rr!QWzCoi6)cO~rsJhIsP6@H zzXCiCmT~aKG6XDpoMSFu&?7+q;KY+Gxm2zn12XI|20-06;>r;0JXN~$$0D@`jR+rP z(U|WnoaPy_7fF@&09BN4LxlJ59F%2t8Fv;S?}-Ke=8jVbO#)stB~GH!QZo^dkDh`` z2Y|ZguMZ=^V-Q$J*TB#>g4!dVgI2vbx&LsHcyfU=uyViM=ph;N`_8mG>4^)lVj{RL zEdL<~F{8`40o=xM)CJY#<#ygC7|{!=t_BOLl^MKA#@oV6IbW3C!%9Sr7sKDW;xy1pBAqb?k2-eu5)sTJR zy6?JKfcg)4NEFmNENv6bm1$a2RIMXJkkZh(9&b1eU@(obXnx1{pP}JCwcpV9zrudc zzwnUse+&%`4V+B=dyCsgb<*}<8qsx01#uJ)Ru?qXje15nvhR*Z8l*PgLLZP#6ZU){Q<;jY5}EL)RxZ#=0~TjXa#Z0e3Pb{c=wh2YN_ zgBR_xNg{OEjQR4`;o2M}2^JykD3QY}TacpS431aJC@6`NEPkNvP=~Fgy2e+vE+VCA zc0bkE-vX!|L5)9ytw47zgUsSjn~DXxN>>Ei1M?@Rps7eWOj!y7BbX9VqD#(;sXGc4 z#rf7UkB45Mg<@~Gbyc9klknP0dvEkV160&2w#bK3LT#$2s;`+@9aV$7nuhSOblZrv zG)8n~rkS;#)g9~IQxL=_@5pJdU8X~#NA)0y53qe=3x;KjC`SX}jMgX_<5G&MR15jt#fYQyyHnaAAMUF(;N_VmE^dB=V(Rk@LtU-To z)FKn$sm&WjKz&IhXy4#;YL+TJK8Mx9ZzO4nKbCASHNtsq*2z10#V$EB&N{VcS@4sK=jVcrZ$`#vU~V?UhLM z`%eQByY{%~_?L@Q{L97vXB5`|H6RyTb31F}|5H{>QR=rmq(|ufqyqCahi#Tv(1L?r zXSNn{>kM!y@Iq?fiEVBc6HIKxmi+Z08I`?YrKpBS5`Wz3dS&Jf(yP1-JL?>ahY_8` z8IKgq&mEAyuNpE(iVI{)mug-%)X_|j9#NY&X2uj!B>dthf^^){CsDcX(@$6xkT=%= zHWD$yj)@K^Pgz1^PiWeLk|2fXVCqx}Tik)u{Uv zJ);UJk^QAhv-V}m(5tiKlLfZdleDt20lO*?ZvqO;bLRw?Ms>z?IdCiRg)6+M1-ClaTO%WU}}r;5^`!8oKCk( zSQSvE7lzNwh8R-k+u32BX%AOICbV|C(TkP=9q3NW;Rjfy=R_0P$;60oas;McGl_mh ziUwjakEE0-Cj1sD66)d-W!vhNs{vl;hMIeW89SVLFMRTfeQF|-HeA;#b6Ixl3f9Q}Uf5Um7hEjuf*RAksOkcG81(+$M z0`D=IleUk{?=nL|lg1yopdTE)yoV;{%8lDo(^;>I4z^7Vgl5B{+Mq$EK#%#`cxcvh zH1XcLM1HXH?eBesBP0r6G3Xb_hT(->9(fllV%xP2mDp2X!g=}adK*Kg32}KYiSfwr@e7Qq_{7a=}tZtaj5U#jFEb2^vTuwM)ltAtv`ManuO@0 zOLXx=PJ_xx_(xLb1lsmY6qVUF$|V@Z9}TS!%~lv0-u9oZ4ioGDq6#s7{L2XfWBxTq z{krR}Q z&;wcD>YrQs;a6n;P-`KIF^=aK0Vy?RBHGJ)8R?=?>$PBj6`*Lfa<`1#(TQ)rUESuz z@$m<`QY7$`6?!0c`%jbc)6X^l4O4fbwU!ysxsN#BA<<=9fW-_JvfdhO>go1yNHJ4y zW$jN&pV3t9y3pB}Sb;~O#guV?HRW5CFOpg@6erjv8q0Ge5r=kZq6fKh*O~*4slrY% z@`GcgZ+LF_?eH=Qp?*r$-DJwqZH-Jt$&rL=3-6$`{04hmFI)m*K^g5MKY5>>k;4S$$OH#HL}zqor>VI__!T-*Q~3?z`pXi1U65z&e~gze z6U^gaBpV-YNBe}BbkgYS)No~FNw9a$&kKM{S#lRo3y^uKo24R&N0*bKWUmTC=5>}h zcaxuH(7CuWYa;0;KR6f(*#HPuvJbc?u}4O)04O$Rdji`6RC2Hck(|$Ow7}V+t~0#9r0u#Az|VT;9gf48lx$v<=zn zJDk=7vzFHcjWYtTz63f(mf{eowYvkKVOrLjHhG0o3E1E8CM;%f+wU{IS(O72E`Iz8yk78`O z5Xaq6M1URUdkktN*mMQ!7fyw=jGIVHBVZ=}FL+#k;rjg7`QB^{Zlojs=}xh|e{`q} zTt|mZ!yLvo&!&am9S~&z)~ZH5pGf9YQ;=7=%;epRS=rvWuvJ`Jv39pOb$ zZYq49esEX`FSEE6HHZ33J*jG7g$>d7!Msm&zdx#%rar*Ad}I(7yLX_bUOeoc78i`O zL7B)#4A=J@beAuch}CvBsV3jo05mOtTli8#1dNTCDp*e~_@L?{?Q-^pmrIqLnSCl) zrv^!sow*p;3e=#l*+9^j-3jf}(}tOlg&c&2DS-qsH^HyjgVB2LSq;Xl76=@>Dff4m z&Ee17^PW;mF}s{N39CnJdDY6Yqm{p0yW#_Zz(R2k#GQ&|BGO@jG|Y=7{|}i*$u-l> zN>>CNn_V1Js2ZMup6-YG{I|^V3YZ^c+^CeVrk&5^>_Xwe&l{CZ?X zG<`{*Oh0eW%3!|h#-evCS<|`0+zs%IpE;*RFgB zCmnDFH`%q!wLIxA!^35;E!{;BTWc^TI@e=5+WSf$q}~b=R^ZMXtEB#(zX-x~95YVw za?zQW=^d_FJ%pkspJUsWs8rXM_65&5-6TSGM!f7A6-aRnUSQ#E&lb(Pb`b@k?Al5$ z=r|m8ErPf;uta&S!>)RDnJ^tGw#v+8%xrVXK#AITOI!773_Qsyy=-Q#+yGP>*8bqQ zSyJ{inu1P;goop7%_9Eun&{|*u=$Iugwa-S`$$5W$Tl;pTRV4_?3xB2 z0|GUe(U$L1xTU&iBP&*!HTuX1vP=znhkb%AV?Osq{ek#YG*o{7uan$vTlg|1^M0I8^?1! zpfDOUUNlteRBd;hws2Bl<>k3&6@x#g8HP-Po z-AQoV&c9^*Kb@v{mST7qo!x~~NgJaLm#^1^MAQ{@nw|6=4FKVvR>5wlqXVp|0yOL0 zVOl&indyaJlTasJq*#LR-Vl0T{2b^lzNrgHjShDH^mywH8r2l{x$YxedgU_ox@PGL zlz+E_szPiM_xR}^QMGb$aT24;3!_LTU4zey=q$yjq^wWbyy~gKcF=}~?uNY8WH4Zb zm;1pN%PjRQdaB!u124Myf<0q}n-dspMQ{NTeP@4=0 z0IGD>hq2{_2_oydXseX=^tAgJ^X}W8lQ`mHi` zM}0GG{b0_;UX>zrVxvJPMc*e4>1u#0WzRu~pH?U1QNQ9is zKn_d?6uDylwtMUG+$pH^K$2w8PJ_Jd^YL`Pc)DDMV>X_Zr}z1KNHC$P*gSlw{#bZ1 zA*#u8c=XPd2f&ESGnJQ{NxZ#ywo%pvxQ#DT=>2@(FH*?&%I)Z4jK_&;tB-fJQKde6 zg2yD)j;T{nM3w52EuI@L+GHAX$xbYH*mjWCRd!5vHq>fWB$-rNtFaIB+&(Ba2<1;c zAotK-{;puB-~4pcWU2gseWx8x^Uz*O^P@EdR9T@$c{g797M3a1yfOSp?LzunlV(K?'yxSz#Yo)M!OJja+ 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�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

ykmDn5+PwaJ=~Gcqb0}>0%Q4{5MUj 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%0DY;DsrBIAyCplSMzMn@A`XhCTztuaLzjQGf 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

mX9_KSXI}{|fNB>y;0+aenZ%fD8h3!w zqh#)!wtP{Hrc3W4om?PL65R@GFAnSbjzNoRqQ!J4Wz|xa-u6&^bI__1+M}POV*iv2 zw+3>=9g$a=Mb{{_pcmK4#_vaq8^G*0AJ=y(oZ`TXvMIyH-l>^PIbe2-A zms$J|#xK@8q`NB2E=cAXcOQXk{TfP_?p-p_bf7nqyeUH;+OGw<>4 zZl679`BHn4#FvyiK`I-c_p-tQ*te?~sPdX`(gU}=AY(#F$>`Z9MCu|9r$AYlR00TK zc#$SooJ1GtzG6X*ib@4?%V+N=`a*bSMke;D09?(f~zn=35R?iz6TVf0AXIws)6YeYq*#3VHc zy+@w&(XCNoGsvzsx5b-hcl*!hkEs5S%Q;k;_O+*y4K_HX^^AnJH>hf^(I{tDlA;ahL>pEpot z=3Hc#S$>@<*uM6<=BN_WbU=>a&X$rkt_82S#4sOv2XgBl?@@$w6)`yTNAt_cwsQpl zJ$`?Ae)N5WL0O~M?_Zt1#9IknkKsMz7gPJt{hFpoi$g~TOm#JIlng0ZpgNIj%D^&XFeN*_ z_ecXUssi89(3dajmhrN*X3yMo#?+zezH;-%9JJ=EYUZ9d(uBXWC=0xW^VJjaA9Qo5 zOw;VrEz}BKT~cxyY!D%OQFalcgala8_brZ}$nvDR1Q###B_9$INa@F9Pm-e#!{5Y} z1P!UuSyJyN_Nm@g3A^0ZO&uqo+0o(tj=#=;c0h`+cGw?x2OU@D@$fR9BqJAvjPraL zPwU;o1GNXE5i7Y3louc})nSX={c!N?pSyDbk_j*&wr@9ncrATUzstSHBb|r)`A<+wy00qzoX*x1S>s#sDU0=NQA5+wM(0cr4Z{0yY z)V;!zl*mGCQ+ZZ*_V9Zr-O|W5&4vg zaVAAd3$n;YiApo5uE{)Qz@hg&M!S$IC{V}?W#AcTYkrK^azON&EKzQ zn_O^!F22za`mm2rcp6}a6hHzl?Vu(zL{yo3?f4qAAjZWA!IJ!}tbSw*N>QIN0uc&> zc}8D7?Uz&&s4c>U4D~?1 z;8h*WiV^r9KA@<*V=V{9!~y?pD@Y|NctunPE73d@I4CLPPLdCBNTw+5>=oz|MyXZz+IrC+t-%H{VOy+^Uw2Y_7e9ai|zU=_k>nqBvCo7kR$-(m!>U*S1NHod3;Cm`a@z{*7cMI~S zrlY;NvrDMw)G3Ew)X&mtS9Dv+Zwm;1iYh9iKv{9iMM{TjoJABq+F6(vlby6I7s=zV_nv&Sx7YiN)7&o$Jkv`lL}xmU3*ZjGDxt|4 zM(F|el906q5VOfS*f!0jq?o<-7`L!9jTaf;07T}H7aTJ^m*(z`MKPSucZS>3IGZd$ zJl}yyL2kW0+?LU9<7h$qoh)XGmTI6oLl#umDF(m+>`fEY=>UF-UdX73@9gb9e!R2$ zWM}Wo9W)^>fgHC9D`iK^+c4VgYuKqK+n^Th@DhoxQ7JD$xuBb(-iPW#_Mwp2LU_X{ zT4WeYD8l6Bu03O1_-D6P{kzR`zbB@O&g@=KcK`g9$?iq{ji84C{NJ6?5MFAfv(YK6 z+U|J-FYN2);_TpW8V@(z1qYy4l_1ewByQSu&1tY}b*+C$!Kz^SwhL1>%tvbHRiCEE zr2}-c1t!zW5$sa)g#7F61|;ITSSUHozycQ^VwYp7&tkEm-`ClL#*;6+J@1hcms}(h zLLaqNLCip{;l0Q~gzG|GBjgOo(}Z(2D?fy`fm)z&$Yk&zp)i5WV34qL4M^aU9XiIe z2Qd;+Jaq39sG$Wd=;)aN+O*W+H^N&3&`=quU=^$K5@O{Jw_yC>N;puljk*wOzhjQz zJWez7)OEJdB#%A)my*0*-gQua^!B=z!y-$FTt>`B`mnLV22{0{M!dC6=gy9q&&~xR z*lLWb4WXRkO@22@(+NLe*yg)x?7Ck2h=mcNoT)q2UXt-cr%uU8;>3)UETQ&;&YI#M zB`PU$*Uq91a;$$kNYLP;jV@%ctJxus(V>8qy>J&vgAlg8$33EkCZkgWUCrqp#_H~$ z%a&Q_U^NDbpJl&Qz7j^vvReNHj%Gn)(m<*9-~&*{|Q;a66|gM9NwF%#7xy zXMCp+(z$A2lt-fCAjt<3^5-->=Hor_*P2r13KY-VCSO@^B2I3;Pbc36XaPP2#Y4Sm zIEyb6{pD%LF$P)5Fp!aU=#b2m&E&kbxI^gX;Xi}w>14s_Q83tt@7`{aiYH0^I!kQ; zw5+)6jd^nM6;Ub18e^%i2f;+eey=t7XqjoC9TK|hpK{*Ci2QOqM9c5UA(L*AK{oi18qSXlu0I1%c+#xE_)4?kPpaf}yl zVJl(I>uKSsR99(zoFo4O{rexcKJqyK1AMW0pL0b({>W{SO^hlcN8E+p z$xm|Y!qZN>p5hkBpTA&!ufWizZ;k20Q`ts7JT+14jEQaPc6bCc8T2@ihtvZleuZ;# z<6PEMJA1spy?6cyP-Zn(-x9aH(`1GrFB3z+>?0y`H*$(ABk+OSrm9-AjKbqctP$=m z?wXS|NjIn-3iZ8+oXEKdVMeO9+KeraG^*_SqP2GpzO@Tebzpegxl^L37p6M`-QvZp zMCoSi|D>>9E(T?lR*OpCPnO&rVv<(&XpD)0k^C?-DkbX*yCozEx638T6RhkIxC}Nh zt`7Wa7VDO$)L2f7l>MBAb__|`omW{zV1&LESEl(H`zAgi=bfXYk67ZQ(1A0Wkr6lm zZbw5a({7-leQXW37lOjqPJSxl5Tnr+DT)MBg{yt8&KjBNlLv3Dt=8?s>|nAD zvr6`w|8utu&FWj7TYQUuoc({=tti?FAAQ(9SEmK8XHRoCvUo>_c-qRa)?ro;U{q@} zscr_<%A7j*jk~Z@KH}`vsef%|cMqTFSVy0qKm2aqa6eyYj%7`LP~!!f5Yc*@VEzSb z1-FrcGz zm)<4(pVD)Z)H^_3UiP4R(wm+;j22+s|Jd$h;Y0#bCd?*mLEn^b%>?6ujbKwOTb z6dms)@jmkQ8s%Dj1MtJt?KbpX*J(d~6s21&?_1wxJe&VaK0cc@`c+s+DB;x6#z@0fNSMT9kaO zh`#R>6{(^lbatTg=6Slpg!mvtu~D?0xik>6LR`mB%9?(p!Krm=!&nJHa(7NZGXBo30>m8`4!)kmx#a) z&mR2L@x>P%Y-RV>;)mOh&p$zrXxlMse6!VY7e8jptO*BgqygP)$LYG8Xj|Q;vv;G! z8f3q{*G;!;5dHR--PQj|B>zd|{s1E2kw0|DUt_nox4qhaFT3x7eWI#-P*RmRPi0DS z>20M3O80h>b#0fHuGykDw(F=So1f!&d9oNB^JB;FlH_HA6Bmjaos>AZ+kv?-ChG;17ro#6W+3^^2-F{LjAGyy+2CBI`S3IxmwZCI_4z8 zhmP=bb)Tl#nIl2_Wq;vqq5s<$y5W7X^C82&2ng+ZXzO!z6;t~wjQuScwRU%eZ+E^L z6dpr=$oyJG9>6MdzAERM)2%UBfgKv!XG51o4ZPLxseAv&(nW$Q*W77zO@+W_#g-1a z%5bxF?~fyfqh_-2U9EqF;ELiINPSZ>mFZOp&Umuv9KWoUJ`S6`wH`_SsOxQ_Yg z6P6}cqn<4^uV*zVGeo?6g%buIH4>BI%PfIHlYVEwSM%00-UA|Xm-pwhhb8z;OG&x^ek3=Ylb{HB*oMM1EeDOw< zI5SF;t`~f7{!MbN>zYR)?t-vcFUlmaDV9u9`J!ss5AZl@H~iC+@G*Qdz5Ew!3HOt6 z3$xmND6?E^!=Wv+vFero$IuWwT|+Jxj4N z%h(UQV(=+v{hmHf7kj%cbtxy=w`AL)E5t#({cGPl-2U&rx4Zr4KBjQo?x8zcO>8bD zf&}A9P2oC+`CHy0n#Z^~I&9=C&2n_>KlR0S`{AiAH>@`5<0ts*{s;K<9tD3r`0(Mc zPkY!F9T|L$pZ~p)dKMnqR{H;R4 zcW2w?oZ|8K&UERIGKthW`{7hF28x}Ulwu6;fOYAM4L5ND;}B9*FDCWHrZKynn&CmD=3f`4POR%-W>hC z)8#rL6Am}r{fK4mMwj{x*i9~^nd7H&q4hj#{)tSfd#`L&68NFo#Z>2whFRYcbnSM| zW)t}TV{B_OHd?nJTt}7}UvoK!{o##JyD;4e6(fiJ{A`yG=ePWpcW5+uAP*Hay$NT% z>NNb{VPsLC4r7aTZDo*D*9j2F$E}Yv@{blleni@TO!?;8S@>G)2S1J7!uAt4+l1Eo z*WNE&$v_!8|7BamZEd;VJ#f3Q(a>S?P@n%BHVjHQZ0!|g^@%50pXoM7g7KaZN9r#3 zq8e|1;}HA9wwqRE$FV??0!$ILNTE>>`Apq$;7q~JWSRckFcEL zmh~VKSoc14V+`2FI1R|E5FRW6j3`NQA7m-qCVwdJlvo}-VCH*F?Y4S*um03r_&_32 zHN)&U@C_}A|KqR1sT7Ao9)atW*o*vaUHu3G3tD3U82_skha z7GsR@h?D_=SVxqOqft5@Cm5*!$J{8!w-we|p3mOCc@aH}j^7=~jupnT%5C%Q9aSXaLP7i3;P1K_g2Hm-EHW z#>Va4#s=DGxa~KGC$HV+|FVA5e_6X_Zjo{hj25$bG>kZWRgiPqcUZ&8P4)6^`<*Z5 zO%2x6T9{|KMVdrrl*Q$&<6_w?9u|4TPSZ8l@LCE41|8v z+V@^Ka2pv1jJeOk42p&EY%q#F#?u&&L2mK;9iM?1n+)ZRg^Ahd=#x#NE$#ntt@eSe zeLf2Li#e(*hkP0*j9j0U5I~|#=176`$Oy)l9KQ&2+6F^Qa<&9X=Ui1EcU?LHGWE@w z54%-sIMmsmQ+K5+ft0ywiXTn`{w?G2P7%;0P}7I3CPlUTv9$x@gRUl=7H5Nik}RUf zMsEcUTDpj?8x8P{Grd*=-+S>epnaSoJIh@MqnXkE0Gu7RI3k(L-m2A1QhVDfwg=&F zfh3oXsmCIc5fjAC+2(^*h?@uTymh!9{QU>YW?j)i*XMt_t0Efc-+;3?C31M3{yJ}M+D2jmygpd1mNm9SsYjC&mV-mvy3ivhNlOHWU99DmfswG0@3mDv+ zydt;Dn$v+D0#?JYR(PpHf9dYRm$uUeD7wffz^j%Nc?PE`g%S^tIQKnCiaMU+;diFf z;}H#7(E&JVDnnPNZ7Q)%Odus9`E*J*`MqQ|NJb+*O_KuZ(g;pHU7euEJbx5;T?KUF zV2i~Sg>?kF<|0pZAD>AOb*xS&OO~ih)kdeH(O$Rw21oswkhUGaeX4`P!66u302SPH^$pTTqf7b)i(xOnerwRCGap1 zhbO!E8c``bG`CqiMMjyZFqOCT=dLbJNM}%Msoi%s9kpD}S}j-&EUy~16}6*nyscuA z-IpuvltYn7*)yBwf+PO*{hpQgwmFkEYPCSq-xkKxf#6NS=%xu3Y3>vfO>(>_9gR*< zFHsk*o~b@a=WKdcubARhwxzg?uw;NC03xP!T+wt%H#+Tm(R$8@X?9GAq zqM%Oj6njPCASed{Mn%O2DWeV5AdKW=V)Q)O@ZP;BtX)x($*m??zf9SZg0^_cXvpROob>W0mv)^rt@*ca#{IzG~Ne*_;AJAXbSs_{kBNWMer!ER9(vm#!nkaeOKX2av1$cwT zc^Bm7D5P#V%nQ?6Sx|}jLvcDoAP?V(PNz;Bp)M~N z313(1(EG;CJrni{(?e};$J0OS@=NP*P;7H*o85o44H-WaMuZNd0{@6+v=MMs&+tAF zfM*TO=lnK6TQ%be1J!G&J)VZnMJvCZG07^yY|4~W2(3}0maU2Rm~$dbB07fvp-Ant zgkDv7q4>7_Tj+6fbMrl3hD9Y{M9Diy4^%xNGYO}J| ze%bf_)c3ybdw&)-tuicC@Uq&2HmiiVC!xh*NkPTe5^!l%kJWm@p$AnDT6f;>(S|SY zWQr(PON3EXgT?~x^C?8bxFRQ6ja{~^MyZvyj>;G_Iehy*%|P|W?|^-BK4@fQX#<)s zs!xU5njwbM!F*PbUwlCx3#BN;4%zzk)HsAhzb}EIjV8p0^PDq6Xpor?T88N%0C7&BcEhhdwQP^!--h1kBCon4I!%- zr(g3-s)V!!fbG^XqJ-JC>Dg$SZFcCK^rx>WXywU=*OF(6n_Xln z=63?!Rt_vnNjchbq)C3t(RfsHKFu{UDE+Q+;75=)caka*lAPnxwQZY3cBst39u+593^8_Few?I3ZI@-b_NR?g<9GeQYW3U-lrF^ z%Jqs1nO5VF>}MH=-BG1j%aFrOfu}A^QDoQw=7{B`gey5NP$r;6C#3{mRxAKdnZ0;| zS8xA;;#x|xjcZ|d@`3ICzQyckioJ_N<|}U>?8~V%2OHM9Fjl@{Zy=#hjBe~-qrzq z+`990Mvu(#j7sH;(;U17M4vs;$^&2Q`hL$!?ooUo$EJIZ9o@zr01lH%+&5l;*U;z9 zD?N^yx(7Wv40EbyxaFa-nwC>dv_lZg|`U%uCcB13=Zj(lc+2Bs!0pT?I!s+7G3npjNZ0&NWi zX2;6uD*jX5vS{#R9?!*bo-K`9RH2+HY!Jk%+_6yGwmFRz-9l6u#7kZ?995UCzc}IQ zrIlY?8{%`7RW_Bodo+4uX2%|}@iTMZ$ZXXOo@rMgN^SA7AF7x`*3B5cU6Q6vm*4gP zLoLyZ9aW~exngnL-EITtLLN^Cp9W?zb37o+oEtjY zB=j+-+tcauo0YR5bEK)gMd#I1MkDKFQ&#EjWOgYT@#!HEu{q61B z#Gi6@?hL021uv<`T!>1f>S5pd)8aGDuayDP+)eK77&$LM^9Ad_(l+NlzyS zXzlm%@Pp(fVX&<4vPxy&^k~rbP$7rbqVqyg;4%0+o9%n(9QY{UuHRM| ztLt6Jafs(@YYY z7Q;U@EweL=SUwqbELeZl+dI{U2rp)VNCF7C2X#y9ye-n0QtK~(S1UIup1jv+Tq=NM zEN2AKUBGelS8l}-uTZ=S47Af5GL0ErYW%p!z=Wre1xDkxqmub7k9hrR9Z?&#u{*lE zfRk5a!>fg_qJ&G50&4ga}lS8VfR#SuBU^ ziD0Lq%$4zmGgzAKgrQms3DyZ}|IbFMy ziVF3%d@2=PHez4w+wr-h^YUvk_a!pGKLnPlL>qE_TaBc%X=iJ9Lr~LuNx#ytgBF`3 z$LS&^ZC0*Paq?e>iHwIkj5fX4RWyaPl@?n{wQRdZARs$Zc5S0VA4?CQ%Wseq+^mgS z4AKGEc%#Tj6K{O*e@_0d$*y1M({PEi5*EG@s3--@E`BMeVo z!FNKQ%m9J4={mQNumjBJ;surqO6tLOZDZ`eP`gqRH})1e@f??ub;-*|ws3{Q=+twX zSlh%)iry)nw9l(uk|rN-&aJAv@3?O~`0oLQ>&3lS^d3unr>Gsx_tJ;SbluC~IQP~K z#*mzBMFqTxXuP9mk?Xo-Uh%hqG$?9n=pX^Bj7AtdMViGa>r7#?ArGpiuP%*18r?n+ zoq57Nj3iIY=GgK4m&~NrgTYabiQ@ND$)Y5-$7X!kVTc8qSuIoGN~0y0SG{PK7gTNx z^_Qkzt{>jxsfLu{Qrn!YlgeObW)u4C++uZCdrAGPDaMEx$7k{I-cRpyo`Nvw{cfVg5q?x5tl%U;BoodpRSdu9ig=ZRV0EhvBRyV{Qqss2BB;ks3v(sS-_)3` zq6=ik|IQj(_W#LwKH4yOMGqztYNUm`c4Gx8&Hipu*9(FvFZbTmc*xGvS@_DWV@Xq1 zc(t|`pgo=L_e`~lc_F_Mm3bv17r-c*3g3%3HAo1U7YyvvBrnXFV@_03MVA2=17Pd5 zvEta8{D@koX_deB#m#HMF$yQn#M>|x_=5Fsxw~Y4HH6>Iit)?;J?qnKx6dg4vl&%E zQ-aG#d>=M3fr#-pl*!jViPN|cMlm59SXz-goW_x*NKNPP5>|#j_nc418=Xu@(N9=# zp#CSvl2I^Ssil|JByfmU(tEKpw<>13FB)GCN4}M3c1C@XlS23fL1pL1<@6Gtdg)#| zZCaUkbI>~UG;T@QLmR%oTNBos?TM<)yvKwa%Zi5Y_g^~rEc%jN8T~e>fcn)9VuZpUrW4eyz z?zcQ>HoCDrN!0iSxo{gjG%4gf6U1y?fEs^@(PfA1Ii~o(vcuR*AflWnW^iLgvkq@@ zaReT|2i&e&qnR81Y<#Vr9Xve#8!lCLbtsIoC2d~Zc$$B?xERjA z@=Fs=5ksA9A)X!w<=@|nvCce7#m{0fWDSx65OZf;?<=HxPlJXJN3h0}m;&Qq7J|r^ zE*J8Ts%P(H5-em2=^Nsl_+hLDOaE=AasAVZjI)A3M}i`?Q@mtU*mv?_Psvv__Oiu) z=s&TuB+DQ$eWkY&GQ* zOz{_^V{jXoaoO^R&=nRTmPfE2@^!~&c#_lm=X&2TYx#(! zV!~}s`LOu0wl~xfj zVSG&|eFV8UJ~s74okq2~N5Z=mnx89Me83;A322&yGCI{@tL0wR}=xX zdXcTD2W)F&+Tww@vNYz`?qWj1MR0X_NZzP+%`D=ARhqk>^;;eq8c~u|;-RNSvcP zRl#%pl=t$m=m(`+#r`}&H>M8U=g2+??KVmQy=VUf*^T5VkIQCwi}d2Az(K^+-|^z> z4=0#Hj{j9oNWk$x(E%ojN4Dz79ni`1nKzHxXZ0sd?Z}H6T@;E)wo{6}!H26Pt>TZ7 zCF88fN}cUM3?}4=9JnCP{-|;^*Ll%VtaK54nx1pbAlJqSuzgiU#hAJnR?4&GQyZK= zt_z!)QJr%#YpFsK^w?Pk+Wi(wQX|BnAdI{_70mdRn5>lU5DV+wTEn++XSCoSamVz` zHkNRnKfbPMWcb+pJBK-tvInc`eA@GUf_P7U%XyQ=RZr*=p8>tk)qh56_LkJDJV}5wA1+0;Vw7QJ+P)&QBwmx>YVTMH9kWM)hjk}#Lg)7R zt!k+#&Nb_pO>G&Tg^Ut1#8s7_qhT&b7~J3;%RlO>ifR%UZQb|-UHiu7C#@Nl95+{tO!m~?r)3>!Nt97?byR<0OY&gRYvgLBXRjXWSe`{ z9se)Wsa@*|3CwjwHPOP>O4`M#hqlJ0Z9(Vr;oLpm-%-Ipu-$N&?oNGEvJw5{>AF>Y zUDi+#XUTw_bIraW8!#2!%;dMD(!U~0P@39G)&N@f;)er>C}l(};!{G06&M3tWFL8( z+?8d&)0{|Hv2k_PRN}OJXB6kN!`7k^v8A$VYl!320pma%jWf0WlW5a7;Q_Q^BOS$2 z^GoLL0TVIlozemfY8b5bDH#UI0n*3d4}#&p^79*RKLdpH?s+9qI+gGs4|p2;BK@v( zV@yQ1_rzI+mgdm_GyWkK2;_^?sshHc2AjAK8(1jzOpo8d?y&LCi!J-3Y(?IY_Vn7! zA<|{^qLCm^(g4X+V0@S$hK6&<@8?4xjKgsfF1mcn{zNe| z-(&P>X*8>Vv9Xwto9JxNXxF!sv%Rwk3w(37k(`m6XnowS21JTirf1Y@;Og!KJS<2c>1AC&aZs%`FMhM6xi7#DYg+^6K$VS41i$9=}0+<=)J`UcaZO2f#e$n$!9UFZS!^FdP{-O1W&oivSlTh7axm3rx3>+CM#s}O|hLqfL=ry(^iQY zOTw-(tNxgrO_a_#H&Do3>u7otq@5 z3foMca0}FVM2$!042P+DsWmBOtrqivY?#Qf5MaqP=GX?YM-vpZsYE6Qee*BsI3O=M%-xd27ZeC<6pkjB4f=4ws(UmMM%k5p>Od)i zhYpV9Hb({u0fSK-gmo8tX*O+OAZ(YZ`IEujdUK*VzUGLEn-#UqY$|qQInq~c3zu$mwxd4^IG?NbXptg z8sv6}q5R%qE=9jKei1!L+Q;~seuJ>r4ltZyKvv}1CGg%s{Pj)7S3mehH|YpusYk3_ zGX@n=9Ws{HifDRD4VXT-`vQ*ky~^-l)}@`dtvz*AEmFCexW!54ih{M3s4Ws4rMu^n zkg4wC4FaB>UQjj~C}oGsBe($PbhoClTzl@W&EJ1qSuI%yIb&@>;nvWK31a3n>+2?y z8U;YSdcC9-y(_Mjik6{TDT;PKl}FB3G=l9}5=gq=jC~~)L(w)hW%S0$WLsj6T(SBC z{Q4aDEofKbU#Bv^n66gaRcQcvSJ*Rpn%JPNpe%~}C*n=sOZ zyVHv-4Z2~q19uoeKGz{ZDs3na7mN`uWEaS#{Ab11cs>#zSwobfl{Y( z)}R^m27z{jQH(4j1#EZ%nC$ia9T`{mY(H?eIAv&WFo!hhPfoLQ@U~RHe@i)st8<=4yu_&ssrRrQMpdcO5$Q_ zM?uxL8As%RY3SX62?Nz#} zFaZl$?#n~`2t2VBmb3OC(E|Rfw?k4&k+HliqCgm>kj4HIZ-@0eG3h|WCx>&*S{lLUQOu9ZHYeHU z+rw}0jx&H%dNa;q@`2Xv7iyD&`#%mU!FN8!;518*9mGhra!h5KE=hVbODcr7TH%H` z>%P9k&YEs75tSbb`y>}KVf`FK2qzlq^to!tJbgpLl1yZZUs8wWRjgITj)pw-V(`u_ zdsFcEoMOydN;LR(@}qDQkN(;%9sNb2#mEotLD@~?Lc)65y8erL0ahr>hu+5YpC-K# zv8?1U&3-B^L~Q-RRBULIu25D{&?6^2_=A1%)5calfV2*>r-U~mBIwKlVxkAA`WKn zXs+3s(f6w2EY*#(BkFvt*<2~gX4L*rf)Qkgq7NCvcVujC7K}=R^o~46Q@)zOsxN^S zkz)D))^3qUYc8~@a=;6KXTZI`yoD?AxFu5V#oZf9Q~GGr*;Y4=#m)mnTPcl zkpDjM7ck8FI8br9f8_5e&Z7&D3|)`}PguzzZSB0dk`%mr6TQ?6x@) zobkino1o6buogzC^Zi$9iPqqr8>HZ$h%V?c#mI)8VOZK=kX4WcBi}8U`<)!;CMW#- zmJP#jqvFxxL|V`CWgH&0ckwYZ8lb&L0z+-SyZGxBb`8u{>EyZ>XiDWQq!{}_rdk^U z8A7FPm}3tt?j*T5o6s!|7}ifBJfoMf=d~0sxr;H4wp%Q*uAiEgRgOTSMeYho{q%R4 zIB4^kQ&vYY145htm8*wV})FTjx2B6pOq`sL@CsMjPSRbSCK%EC$&w0tO|_FF=~)W677{F(*oYf!v(JzIFTfpX0)yaKA*D&VJu%J|wcG08_ITF@&FW zt#=lyR#9sISxGMa@4QsxM=sDQ(7r2I#YPCFWc-s<2uWj8ldM@+4p!&5K$51rBK`gs6Y(qme z-;z42J*y<@5pOwtc;O(-jNb(+g8M2D8VE|vNWy+N987ITF-s=;KiM|X0;Ln44kSx6 zWPMkR@{;7btaE<`3GKGFY$(X1Gc@0gi>$gxsS@g$Wuhs!8$#Qjto=2_A-94mE-w$&4KaRLr->=uFha)XD*&*wPVsYQZZRi#jciZ>dDKOlPAC5Qi2uI_d z!Afp#>+yx_!sanV$?-T7%&7oWk6Su%dXbB(5b{S;^{c18qhTw){TTYd;8S=!t*IL#>BnzJ#`@bH zeieC^=PV_z7(Q)Nz1UhGZ!%r9>_qJiUiHB%2#Aqf>Yd3IhR)m7F5Rv-fC_@Ch{mSq z{07udrsJGrGAVw-gMNUkUdKr5nq>}Wnpr}iHz!?^_&Ou&Y2_&diJ$*0ug^aryQ{?E zx?F}Da1eBtcg11f1;?@0AH8mVPo%qj} zh%mfAKe3=m_nnM+hPTS<=UqA#Jb%LL!UC-QleI~|6C9I}wRk#y8}eyLBQeSL^ID;y-$;PaeTNNvQVc>-e~=CDubF!x zHmLI7UP;Z@#~_Me{ho?J)u^Y(@DPNr)KeI!GSY8Qf!35&t4`9gy<#c9nDO$OwT%$e zoUmINO%?)OphVxxNTKA~Q}tEFw3I}{fL^TM+Y>zN)?ai>^bY#y)=<{;P@ZEK4=9Dd*W06kyy8AjVn>2*IDAf2*=<~zI%Qz|HU+43qUO^f<1bkUPf+jwwca8z=QS{A_3XhtlLgXWH3c6y%S2)3NXijw@stv9_ZgQYSE1ibt zTI`Pv=qUwtPjZ8Q)}8dN1}6{J62i)ng>lATmO`j1j7?q^AATW~ zWzUIk`Yk&m7%S*RcA~V3*@hItufN)?*h!(8Y${YOR!tV*DZ8h$mN@KsZYCASe2XWf zvLqLJ4gW6EPi2K^HG4>Pa`8J)ItZMEoo8@G?62oAp^T-`vY%YQ%}?3lNvyuz!4KfS zt%t+nOPTZktcMo}KtNdk%fsdBWM=F7PkGp-v0;0_j_CEGftUf1uBs{&>d*tTk63i@ z1zIE3^vFX0ML3IU=0p-lE%oM~|Fccxl9FJP0Wu|H>ByPF!V*$I!rIaC>f$^_g_0gs zO-v3O!=++z%1h!rC0^1uwhJj|+N|N(#D4mC*s(BoHg<+!*=>GGvY?zB`dgE@0hOL) zK$07CP`a2B_F(fcJcwxj7kQG9DCDouJ>}EB+|7h`?K96bDyg*UKi$61R#`|5@~`#i z`X|8@OAG_^w8u~tL0n$`20;WuifaleZPYKNs+H@mO0s_v&6b;#2{Ake63L~=jvVl8 z+d>c35tP~>Nz5_X@_cjIX-UJYGgu(Shx4REKp*>e!qXU=hDE3s5<@LRyCvxbXQV#@ z`UqjA;(~W5;a0x-%K=E{?j2hU%*q&oSD66LL=x%ugkY+jO3Ty6 zNa!nQ+lnoJWwQJxbkeb1?t*Cs)=M_JGw~V-R`hUic!KIVylenMWv8$f&q3TUgDTlH zM`sSSR0dl})Z8$i3+i*7I$IxLg<6d^`k`4Ke?))t5ySZ^FSfX;+IEj47p?prSVEH1 z@4AxrMS*>EsxjW#qH0qn?E-^e*uEa{_{oX^`dS&>ev{vePi2ft#4PJ4BlXf{GP4K# z@LAhI9!-_SYY!Le$c<=_NtGT%1@qFVo7QIitCS+{t~=|*+fTXEo)5VZKaf%@!zBfL zUsAopZ~$xO17e;Mba{8IrM*@7=l6;BI}!P+1`Ls=f|i^C-D6#D`~0B)eo^Klix=(t8gd56 zTYt$?GLM%zXm@jQPRV`l8KP)2kO6X6Nq5~Z=Bm(3b8?6E ziKL}}QpgMyDmW?x??VKZvtdcIZxW=Wk!G@-d!I{k>=y`x45Ql8I#RpFCSxkxE_b~X z?WC@45i9Ie5L7W;>Q&y99PlvzG%B@bO1UIg@vm|>b7-+4&dLuHf=A*=k3qSaX`5Z= z*1UrB(g2+JddulcSlHB!l#vKuE;yVHjVTFHovb`G+t?||v^!!RA~_PCA>#|GUR9ty+?fNQMVees+ujGSOg z6AJ|jY(d>KA!`niseuI&%Z*kQOSaGgG>adW@{7WsdR;)6aCPRuF?9&jq<2{7EfL6e z;;(DZSm#>?7d-C?)Z$cJ1)_&AwK zdPAN)H@}#?zTy5m2tdJek`h6IfY2dv)4*)tdz{IdP!Y+&#&3WC(+?EXbT z*M|mdEnv-;j&N-&0+`{smhUpJt5FO=jA(X#)Li6W5NIKVf4_9+b!Sc$Pmv+LnIBJO zaL8WiiP&@mJ1=k(oB=+&);bFeKs8i(6z;~IIUn_#LWb33i>$HDDgx$`rQT##YNIm# zK)$sdDbtAfll29Y$cI%{Br#d2@#CFEG-wr$KP_{rky%QbQQNMm+e{dB6krh)zheCm z+=b@OX6f8~A2Jh5geAnfRgC}#rnMI}Z7XfOEeFM&i^%u1vT?N+I+e&Z#V_EV@)8C# zV~iKM8%s8^x6U<{vK109iwQ4fR4c*~5u0ENA3}&#WAD~+9iI0(6MEb70rFfI&h?{t zXI>hqLud4&XGdB_&5Nu71v5ki-{lk5`3tr6arp%-GiDO{Aj#&9Lr1&Z3JK~ka*`Vr zM7fDs#vXWaOVDNoGI$0sITU+0Hcr}dzV?&j_fbqP5*r~~G^gAKejuh;WPSk5@%@(r zdbL+=n3F{#D}C;=q4{uL9Cbe`Y9H|UO#2hfj?XK6ubWn!SL+7+BBptRI3E7}iHvg7ec)MrFCG7im|S za=AxyB%ztS!0^<8+DP6EbmF06at)ur4?Z_Of=iJx28lw3*z`FkQ@gsA7oA!RCR-bf z84l`XlZ904+3D%}(Fermv7GVqt;Ub zb_IW$7Knng!pT)VzvY;G7xE=KvvQe%{~Fm^2r`3lzm+5C8rjIlFHM&p_PxG~XBMXE)xE9y7J zmPLw<*$Mh=SR*AvH)_8B`Ta+BwG6NEIkx*IyA*{_USa}Q{*fQAOE(e4E7cbTEz@&z z@~n(9*F?z%G{apYL?LkIKzPyVvXiWBvdYRW@&h;jQgXmvrO7ltB_A`SJ}HQfbeHl? zdE3U8QD?!R_b7wuyF2jvjxIthZ^HJ0;lGF3Z}X(boB;@Ew*d$U<9`hEew%Wr=jUA@#O^+h(u#=}FCu@8qOBA@zrc(=J!~)H@NK%` zv{iRodwk?DEV7r`=T4`n=q)=b;@6+sCw>pMlUbh>vEg2?{BzbZLgf~IEEuweTycyCuO;b zr;2~;l4Q~+fSz9_e$Me8ki*ab0VwnSi{JyF|5B^|8kHfdK2%CI(UyJpw@F$;65i@; z5upbp-p(b(Tuj1@%sy@L0USAE$=Z&>f99eSc~;5grx^hJ$6-8r2j z;Phkxrniy01WooaMvKi5oA-?uS;k6M@aEoFcYBG7R`OpZR7?d5xF$(PsnCVp@8+b! z+V>vkk!|&i2G)tN!%7S4RY1N%_>m%x2(Jg_4_$te$okqC#hjSqoxzR5{DFad^pE%Z z^$BRdsFKx)OexCi|g`R~>TUg#1?X9y^YcVb?Du>mF9c@e8`}+AQN6LTVqJog$ z9$Ej!+dfeavRy_ND=ph#6)9*uEEidDfp(c^tQO&b8&yx-j*c{a_Q^l~50%iyciWy# zl9~U>S1?lBrR?d7si#U)M`F1}T20X^gV1R%VCJ-1%tIjmP142b<>B~z2ZHbYfy+5x z)03&6*~!J%BRM1R{rY}+HFw7IN;S;K(rs?lp?~z?@K4TpH$6R40%C z39Pgy&_MOw(_Qo{hzJpFKd_&+k6qqYEQluYc&IS)+ZFSv8&C9u;bR(`Lrbg^m|*N? zOicYN@TVVJ153#Xdd3J37|Im?%~EJ$Lxn+B{o&1PUA0!P| z{w-dmxsL@cd25D51FCr2Xt+ZS0o)Hnqnr2!@Mw{3IZfD!D2h$|O4%kU9~ZP-vQ+)x zR}vj7Up7#XI?x55z)Qvlj*c99OEh!J8GWvZ=rmFyR}7WenmVTiZO`jX5Kl zC?NzJh#*N&Grn+Fs<&@b#okap*PsRN913P)fiY1H-; zqigP&G?vPq_D!V*LG-C*0AsAeRA3vrGQv8Z?jIf4!z^oe2TIW*jxIC%_HWG&M*#?V zb9s2_5*U+f2o}_;A(ApmSELW#tDz<|c`bcGTDnc{I9~n&_G*))ALO|WwLM^%$*Fol z%GsdWR{&_I1AH138g#Tzc$Ob1^~j2!{F+F-S06B_nfy|V2YpD>O4yXU0*M`pvY9y8 zD4s8jW|qhXqN$M&*(bBbMmr&pM{=RJDS5X{oGU8lEe=p9d-tB`eUX z=xAu1Nhlc9s)aY?_9)ZokH?E9FjI;V7Sl6+@vApojb#%*`ui)SA)?`c#jwV1df|Yh zgyM$Ym(%{-*AQFtt6eIM=EbnLb_>DxMU6G4nLfF5~-4-ZqTG zg$6jrDVS-(kR)P8W;Jw{##Rg@p!P38zjs9OtYRL`TpTMHLK>qaib=Y!g(H{X6yZ2# zF1hlyU#;1+J)&L!ak;Y}Fi~9c#IVn_9T9=}5#sG19K=4YaLxOPp&<{#7BWly{ocuR zFk9C&Q4+mH6;rz(+HbAhhlqHOMOiqs*Zpt$4gz1v>Wk9_dr7!p=>8H?_)7+Y?!#WM!scCrJsZoTV2@^%H0%R6H6H*Nw^a}PA3mLEw z<&TIeZ3-}zcSF^+rT@Z5$5J2!4?{^9t3>Qi-!8$@vPVpCGhqkV2;>B|8BOei7|DnH zzBdeIrJ-OdD34eh#&BK$8VNPRbF-JHrL}O35bC*469s}5rZFUT=jP7bgCKFC30i+E zVS_>90h;CU<2_xht}C36->8=$j+7?^Cmg>!Z)miZ-@q5RE5ez#Y?G8d=QU%tpJQO_ z-g`a^Cy&wuGy-GhkA&Lld zm__N?HdMUF6s&!gDIFF>unV4_SF``f3n_l!vZzjZ@&rkIBl5&wh>qx@7b1sVP3n(~ z*J16tZ)bR9nJIX*3)EC$Zggo|pp2>%K$dZ+>EaWg7rk0}~y+$M9>9CGyX)wU)a)Ihm~=epVUm>BrEW6(U<2SDOuu5a)!T{<@I zQA_bT@rprQxAIaI&rO(LLf^FYzW0k|np(m_2BX_o5qwKZ_c$H#o7E6VIJ2?`=*@X* z6jaUI{{IwPeB}{`F8suQfF3h((Oxv*^K8-y07^wPjYj>34wGY@lf$wAQ4ABRN2IMK zWPF%(g~#4deD47UWgapZA#b!lU2mJS$VMikV*@AlKE_30Trt_bxD1MXGB|TUN zx5RClbSM5AGp%*%S?XEAc}w+b%Aw}^(fKvpd;vHwpCKG`0l}Y;lUlXl>M(VsaYp(D zQ8T?HM>u&{RkKqq#wBo$i)IYl5g$@rnI~{wYL>zi6DJyjJ+?0>7qEJhCX&< z)tBx!2&qY3Ux#A#Jv}d5__kQQRJZ?bUfDmNt$Gdgb+Py@5AOEI+&XiwRj$29w-!7x zi#SEPZU02%IOk?s-jc9NQFDn<*(`e%+>D9`NY1gdrp!Fjh`gY2?Js8#1>@>|E%?)U zv~}V$#iB$;=XGLii9nTs6)d)7^w4DF%mOU*oqWNz-E*RB1)DtS$p6N1tSb(DB^JL@ z%6T-UDxk*H%X1yRp~gvZPt%NLq<<7iB6#{R$jx?5@Jy zxAt6q?B~ppl{8IFcFRY%Do?cx2Sn?^>~<@wzp;j(FM=}Mve%Ryz%sdh5!ur8r>BU# zrTNf2Ev;xvT;l=cXl&{)6=fpzR@0A?-D~XIP@C0AqRQ407 zKZY*)V-tS~(f-69%l#;b=p}<#wghG-rb@zFG}HvN%H}Q{F~>(kUAu!|*Fa4ngP>`_ zPltYftM+niA0i_c1=V-`^yHe~;$1s)!MZU-Qk%Ha^CFrpvjZ34(nd`shhoYuiE<3=x9Yk#%cH=6}gWaY~BlFiulMBqkBu zb*r~K^!=)c@SR9aZ-k~bR3L&LX)N#kEnLB_{xkvPH4K1DCp5co2ad^dS_Zlh1vu>+ zc$u9ei?I#kT`E&R&SFpJ3dn-Txl*U}HB?e(e=)QF6Hf1&*07PRE)MMX)ceQi2;;WL zaVEXA>!Okvj@qH9{!oBaBS~=?%>HN`x3JU`k*l%7Q!agf{NEdIF0QUQZ{-N05!YbKORV?!6tu$+vJ0;iQa(pXFirxg%ZulLiU>V6@6v74pP@k*f zTp$JnCu}9jL$`WFQo@_iZaT<~+Vx_nY?ZN^tL0&0B?rUlQ>{jnpEu&E{d>dLtfsXU zgys1y_6j)#0+y!4Zu({Rg>=4aK6NnK;_5Hx^+K}*DS1@cmbGJk ztI!g{&{L+qf?qtCjKsBFr@r8-=9>MHYTmZvEO5`XmpFdY#$aL+(4QYIpl8}q>TJ>( z3W4xFMaFKX6AD!(^f)ZD?cO@!XX>S6&0DQw5F&)_7mhHnSt%kF8mE&`jto#|iSHxK|EvWo-Yv?(Iw6kbDpfL9`1v7}+nTygA$^ zqc{cy{wS|^m_6RX{I}<=7DdgfO&r5v$C8^<(%P)jOxEB#qjdkFC8H3crx|LMbgR3} z-8S#LbXwSwc{*OI*3JVSN-?TD6*dj}<=FJmC9`CWcy}lG^bKJSSdo*juys%JjnrBs zT7u#JM=k*6)j~CKrYw@=&`ZB-IHk-VwZ?>*q^PrvL=7L8G8K$HEf+eO!mdhn@udM1 z6y#3srty5J(Q)tM7PP$&S|bo<6*Cs~am({e7$T z_+0@mV7Yo2RD#QGO}ce&&?$KDh5wj-mirtsSCk!n3#R*<37EU_J3|EKHDM$M_NNh8+B+7nq!cg30Ay~|1Fg>Y@g8beXspbLA_)>(`0Nt+@CKv$W0JU$?U1QGJ^1%eg($cvVoOLcOSdi4G-&8N~`Tj z);ykq7{f=$7W2IRqbd<#w~g5#RnTwx>#o*I`pqcH;e<%QtHDV8khNZ>8*;=uE{==) zUL-=biLWE?wV&dyiz3HbGiJI$28cw2E1+cUnFCu(}TW+ z)GPvL`QHkk-lHhQ2k`trmt2NJv8!j*2#v2;a}f_O3(<0}s8xk9wR8y1>QtTi-Q=#@ z&?xd^N^W@pS1wdwkwit|e+4irSoM-!Oh#0lf6H5!1kOx9*}z?5bh0?@2dsZFgm2&m z4`1iBP@GuY%)dyC_z1kek@E3#e;<*0dA$R_B9GqgYIUu(oo8nEew;jV25rxoSf>Q{ z6H~^mhVkW6?41U%Az|vcphxz(5yL)vzwHrV!mX}x>IJk!Hm*JzV;VJJ2{STMBYl8< zpc(LBa4#03%hl}I%EC6ne+x&#1i@?DopA@FGLDa2$lQ}odMzo*14o6t zX8$N-JoLw4Z2b05HaqW{bUQJe0?bAUW-ItsO`!=mNlYfJFzs8+;aHk{1ip03XR0ts zy!NFxvFF<1EMyN3;f;!jWVX#5PIONkb6mZEN~lw+a}JhHFpkOJy>&e34T>g=YB> zr4tD=*gqJC_3;rV9oZz$c-FpzwP?Nu2l3bwh)6)%fU|8Jm;h&I3MVovypZxQUpS?R|Tljz8)~zhTWMk4Mx^Yuk#f~odAF% z!dx~{zT529{Xp{{;{cpIljNq}9mnv2tBr-q^{1ljqF&xmt`H&iKVuR%1)6bYzh83mf4T`_)G$sl;kZ5N-c0a2>nDo=MglL-n8n7~xbqxbqtgut%Svh@ z&{VKkGzYZY{5di8705?)H2MA3_M+Pw1n)gvOttlJCBy*P0TV%%ALlB(1J&p=gFb(> z^<=K24*D<8Y)Jd)n`H5v0VysPw+_+Odi*X0noxkBv< zYfAhbIl1&Qp{HPDWT1Ht2a!7fyJb+tQKk^(>;*?XSJg>260-8`6B_3X!>F(EE{9N# zalrH^^kn>^d&Dz#0qPW`9&04Iy(Q+W@8zNvRP4V-)p%i8XU{^4Kk*9`c=PiE=9GZ# zPfNBrr6Ri#sUys(FgvO%0X1&V%D$dAP;bMWRf4$A&0}e74V<3Plsw5AXJ_;lmeloi zu$T@%bQI*xQ0;YkQ-YqGN&R(n^o+@%T8agmNIpW>->hfzCJAhCH2Y@wRy~j$#14=| z;xJ=f*T_2e?Si-J(XGq7>x3cY{umExrg_?>^tl+Y3l_ACQJqVLD9iaA`;{j3ZTyAj z%DU=J##7^YR8+^{c=rU}h@Mu~hDp~0s%?Vd+HC=*B!p7oP$XF;aziRK$m%kxaf!&d z&O8_ZXV7S2AN5kYa4IN~@Nv^4FO;O8kOqY#{u33sUO~$EN326df*_ z#FIk@B}D&jWnbn`Je|M`I(~FfM#yl+X9PR@eEf$x4auCoZSZ^xeE>O$K!u;1Plu7i zKv#ZG5G+uB19(h;fcE`(7@#y9ygbIovl|EXHn5a7&p6ima?NZK6xz5jr}4H~rBdAL zs8KAn+=N+B%2zW4=fv8ph^YgC3&Hy~ zjmS$?xg-;z zdoJPqHjfToU9NIrkMGlGvvs~y@~UwT02oIT8W1oX?Cj|xOK>~QL5%IVAsN{6a?X|f zJ_H5CrD2h&<&xKgUv1dFZe(I~P3;&O3Bc$PgHBwQ_KT0)1C4|}efKh$~b%h+PIBRVpRvivZ0hmn3A`}r? z8+m?DkK!^4Z;TG|9l0AypaF!viQNZUmwuRg;5UXsfW6j(4nzGpkTx04U zg>iB(<%#KZ6BDdaYD9fd$N`3IY&xhq9jLIqD-PT8C-ARjv7i|g<{5y)Q&)0g;J;ho zuq0l+sC6vD->kxYZbad}$pNHn^***a)D@*y#@{r<=lXvDRQgM|I?ty2rAw+_zX}rb zl{sX}>NHxup~?3Yo48z&9a!h&g-#lhrhgdntAt+Nb}S6KG5QZNnG}~{8DuAQjm(SB zGgGHB2KLjGvq{Y{aApVz_kP2)y#2~0;9$E^6~}}2`J;#u_HFb2Vr1=MaZEt;+_Vc) zZq@uX9oUB_QmO>ft)9pwgQNAj(YvxeogGU)=90ufsd2!|vYO=j(v#rCu!nG$mILZ> zfgb}aI&1!TP!za?x>k=QR9q>SzJz5e9#d!zX<=0tQK#Mgor;>53k<6mZi5`p zYMhk)gKw(0uB3C4XKLM*dGM_p1sJ^=or7*?`sT_n+g@+x(*gh#U4}$eix^$>OYO+j zEJQom(yna%InuyoF8!6Zk{Ksd}gecC6lTGqDTTpHEV z?`XEnKKLe`vvZ)F-8K%(iF;uXwB)@aUCtltI8b=j|Bs zFB2{Q*OC!>BC{;iwa;kvxcbZ&)k*Ch&{$^^JP8Vi&J7#3rV#btuSqz1Lpm}$qR~T1 zHv_Z{MI=0WFEU~@`p=`%h�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 literal 0 HcmV?d00001 diff --git a/pythondeps.toml b/pythondeps.toml index bef88d49a6..061f0cff0c 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -19,12 +19,12 @@ [meson] # The install key should match the version in python/wheels/ -meson = { accepted = ">=1.5.0", installed = "1.11.1", canary = "meson" } +meson = { accepted = ">=1.5.0", installed = "1.12.0", 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.11.0", installed = "1.11.1", canary = "meson" } +meson = { accepted = ">=1.12.0", installed = "1.12.0", canary = "meson" } [docs] # Please keep the installed versions in sync with docs/requirements.txt From c5974346d7a7300f5350ccf7af8d61996d9bda6a Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Phi Date: Mon, 3 Aug 2026 01:03:44 +0800 Subject: [PATCH 638/860] rust/bits: Align SubAssign behavior with Sub Update SubAssign to perform a bit-clear operation instead of arithmetic subtraction, matching the behavior of Sub. Signed-off-by: Nguyen Dinh Phi Link: https://lore.kernel.org/r/20260802170346.3493821-2-phind.uet@gmail.com Signed-off-by: Paolo Bonzini --- rust/bits/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs index d1141f7c88..60b63f969d 100644 --- a/rust/bits/src/lib.rs +++ b/rust/bits/src/lib.rs @@ -320,7 +320,7 @@ macro_rules! bits { impl ::std::ops::SubAssign<$struct_name> for $struct_name { fn sub_assign(&mut self, rhs: $struct_name) { - self.0 = self.0 - rhs.0 + self.0 &= !rhs.0 } } @@ -443,4 +443,11 @@ mod test { InterruptMask::OE | InterruptMask::PE | InterruptMask::FE ); } + + #[test] + pub fn test_sub_assign() { + let mut op1 = InterruptMask::E; + op1 -= InterruptMask::RI; + assert_eq!(op1, InterruptMask::E - InterruptMask::RI); + } } From e27a401d2e5a78a303f2c393a289638824e8c0a7 Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Phi Date: Mon, 3 Aug 2026 01:03:45 +0800 Subject: [PATCH 639/860] rust/bits: Use checked_ilog2() in Binary::format to avoid panic ilog2() panics when VALID__ is 0 on empty bits. Switch to checked_ilog2() to handle zero safely. Signed-off-by: Nguyen Dinh Phi Link: https://lore.kernel.org/r/20260802170346.3493821-3-phind.uet@gmail.com Signed-off-by: Paolo Bonzini --- rust/bits/src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs index 60b63f969d..5769bc761a 100644 --- a/rust/bits/src/lib.rs +++ b/rust/bits/src/lib.rs @@ -215,7 +215,9 @@ macro_rules! bits { impl ::std::fmt::Binary for $struct_name { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { // If no width, use the highest valid bit - let width = f.width().unwrap_or((Self::VALID__.ilog2() + 1) as usize); + let width = f + .width() + .unwrap_or(Self::VALID__.checked_ilog2().map_or(1, |bit| (bit + 1) as usize)); write!(f, "{:0>width$.precision$b}", self.0, width = width, precision = f.precision().unwrap_or(width)) @@ -412,6 +414,12 @@ mod test { } } + bits! { + pub struct EmptyMask(u32) { + NONE = 0, + } + } + #[test] pub fn test_not() { assert_eq!( @@ -450,4 +458,9 @@ mod test { op1 -= InterruptMask::RI; assert_eq!(op1, InterruptMask::E - InterruptMask::RI); } + + #[test] + pub fn test_bit_display_empty() { + assert_eq!(format!("{:b}", EmptyMask::NONE), "0"); + } } From 015306dec468ee29a5710176d11eaac9a105b117 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Jul 2026 11:58:17 +0200 Subject: [PATCH 640/860] rust: update Cargo.lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply change corresponding to bff64138882 ("rust: fix incorrect dependency in Cargo.toml", 2026-06-17). Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index cbb3ca15f7..c94c2386d9 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -184,7 +184,6 @@ dependencies = [ "glib-sys", "migration-sys", "qom-sys", - "system-sys", "util-sys", ] @@ -431,6 +430,7 @@ version = "0.1.0" dependencies = [ "common", "glib-sys", + "hwcore-sys", "migration-sys", "qom-sys", "util-sys", From 6d28fc60891fd5643f2fbc8bf4c6ff57f21b5070 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 17 Oct 2025 14:06:03 +0200 Subject: [PATCH 641/860] rust: switch to cargo subprojects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let Meson parse Cargo.lock and Cargo.toml and figure out the set of Rust build dependencies. For now, the only change is that subprojects are retrieved with "cargo_ws.subproject('NAME')" instead of "subproject('NAME-API-rs')". However, just calling "import('rust').workspace()" enables extra functionality that operates by parsing Cargo.toml; it will be introduced a step at a time in subsequent commits. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/static_checks.yml | 4 +-- rust/Cargo.lock => Cargo.lock | 0 rust/Cargo.toml => Cargo.toml | 6 ++-- docs/devel/rust.rst | 8 ++--- meson.build | 3 +- rust/meson.build | 37 +++++++-------------- subprojects/anyhow-1-rs.wrap | 2 +- subprojects/arbitrary-int-1-rs.wrap | 2 +- subprojects/attrs-0.2-rs.wrap | 2 +- subprojects/bilge-0.2-rs.wrap | 2 +- subprojects/bilge-impl-0.2-rs.wrap | 2 +- subprojects/either-1-rs.wrap | 2 +- subprojects/foreign-0.3-rs.wrap | 2 +- subprojects/glib-sys-0.21-rs.wrap | 2 +- subprojects/itertools-0.11-rs.wrap | 2 +- subprojects/libc-0.2-rs.wrap | 2 +- subprojects/probe-0.5-rs.wrap | 2 +- subprojects/proc-macro-error-1-rs.wrap | 2 +- subprojects/proc-macro-error-attr-1-rs.wrap | 2 +- subprojects/proc-macro2-1-rs.wrap | 2 +- subprojects/quote-1-rs.wrap | 2 +- subprojects/syn-2-rs.wrap | 2 +- subprojects/unicode-ident-1-rs.wrap | 2 +- 23 files changed, 40 insertions(+), 52 deletions(-) rename rust/Cargo.lock => Cargo.lock (100%) rename rust/Cargo.toml => Cargo.toml (97%) diff --git a/.gitlab-ci.d/static_checks.yml b/.gitlab-ci.d/static_checks.yml index 61fe2fa39a..568e56e120 100644 --- a/.gitlab-ci.d/static_checks.yml +++ b/.gitlab-ci.d/static_checks.yml @@ -55,7 +55,7 @@ check-rust-tools-nightly: - source scripts/ci/gitlab-ci-section - section_start test "Running Rust code checks" - cd build - - pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} fmt --check + - pyvenv/bin/meson devenv -w ../ ${CARGO-cargo} fmt --check - make clippy - make rustdoc - section_end test @@ -69,7 +69,7 @@ check-rust-tools-nightly: when: on_success expire_in: 2 days paths: - - rust/target/doc + - target/doc check-build-units: extends: .base_job_template diff --git a/rust/Cargo.lock b/Cargo.lock similarity index 100% rename from rust/Cargo.lock rename to Cargo.lock diff --git a/rust/Cargo.toml b/Cargo.toml similarity index 97% rename from rust/Cargo.toml rename to Cargo.toml index 0d24eb84e1..0566850ece 100644 --- a/rust/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] resolver = "2" members = [ - "hw/char/pl011", - "hw/timer/hpet", - "tests", + "rust/hw/char/pl011", + "rust/hw/timer/hpet", + "rust/tests", ] [workspace.package] diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 67ea84539a..af142c4a69 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -26,14 +26,14 @@ are accustomed to the more "normal" Cargo-based development workflow. In particular: * the set of warnings and lints that are used to build QEMU always - comes from the ``rust/Cargo.toml`` workspace file + comes from the ``Cargo.toml`` workspace file * it is also possible to use ``cargo`` for common Rust-specific coding tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``. To this end, QEMU includes a ``build.rs`` build script that picks up generated sources from QEMU's build directory and puts it in Cargo's -output directory (typically ``rust/target/``). A vanilla invocation +output directory (typically ``target/``). A vanilla invocation of Cargo will complain that it cannot find the generated sources, which can be fixed in different ways: @@ -475,8 +475,8 @@ files, it is still highly experimental and is therefore not used. Therefore, external crates must be added as subprojects for Meson to learn how to build them, as well as to the relevant ``Cargo.toml`` files. -The versions specified in ``rust/Cargo.lock`` must be the same as the -subprojects; note that the ``rust/`` directory forms a Cargo `workspace`__, +The versions specified in ``Cargo.lock`` must be the same as the +subprojects; note that the QEMU source tree forms a Cargo `workspace`__, and therefore there is a single lock file for the whole build. __ https://doc.rust-lang.org/cargo/reference/workspaces.html#virtual-workspace diff --git a/meson.build b/meson.build index 40e3f4ec44..2a289960ee 100644 --- a/meson.build +++ b/meson.build @@ -130,7 +130,8 @@ endif if have_rust rustc_args = [find_program('scripts/rust/rustc_args.py'), '--rustc-version', rustc.version(), - '--workspace', meson.project_source_root() / 'rust'] + '--workspace', meson.project_source_root()] + rustfmt = find_program('rustfmt', required: false) rustc_lint_args = run_command(rustc_args, '--lints', diff --git a/rust/meson.build b/rust/meson.build index b6711fe77d..1622a50a97 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -4,31 +4,18 @@ else message('Rust enabled but it is only used by system emulators.') endif -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) -subproject('probe-0.5-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') -probe_rs = dependency('probe-0.5-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) +cargo_ws = import('rust').workspace() +anyhow_rs = cargo_ws.subproject('anyhow').dependency() +bilge_rs = cargo_ws.subproject('bilge').dependency() +bilge_impl_rs = cargo_ws.subproject('bilge-impl').dependency() +foreign_rs = cargo_ws.subproject('foreign').dependency() +glib_sys_rs = cargo_ws.subproject('glib-sys').dependency() +libc_rs = cargo_ws.subproject('libc').dependency() +probe_rs = cargo_ws.subproject('probe').dependency() +quote_rs_native = cargo_ws.subproject('quote').dependency() +syn_rs_native = cargo_ws.subproject('syn').dependency() +proc_macro2_rs_native = cargo_ws.subproject('proc-macro2').dependency() +attrs_rs_native = cargo_ws.subproject('attrs').dependency() genrs = [] diff --git a/subprojects/anyhow-1-rs.wrap b/subprojects/anyhow-1-rs.wrap index a69a3645b4..fb4603e74c 100644 --- a/subprojects/anyhow-1-rs.wrap +++ b/subprojects/anyhow-1-rs.wrap @@ -3,5 +3,5 @@ directory = anyhow-1.0.98 source_url = https://crates.io/api/v1/crates/anyhow/1.0.98/download source_filename = anyhow-1.0.98.tar.gz source_hash = e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487 -#method = cargo +method = cargo patch_directory = anyhow-1-rs diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap index a1838b20b0..f8d7c68da6 100644 --- a/subprojects/arbitrary-int-1-rs.wrap +++ b/subprojects/arbitrary-int-1-rs.wrap @@ -3,7 +3,7 @@ directory = arbitrary-int-1.2.7 source_url = https://crates.io/api/v1/crates/arbitrary-int/1.2.7/download source_filename = arbitrary-int-1.2.7.tar.gz source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d -#method = cargo +method = cargo patch_directory = arbitrary-int-1-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/attrs-0.2-rs.wrap b/subprojects/attrs-0.2-rs.wrap index cd43c91d63..4ff4c424e6 100644 --- a/subprojects/attrs-0.2-rs.wrap +++ b/subprojects/attrs-0.2-rs.wrap @@ -3,5 +3,5 @@ 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 +method = cargo patch_directory = attrs-0.2-rs diff --git a/subprojects/bilge-0.2-rs.wrap b/subprojects/bilge-0.2-rs.wrap index 900bb1497b..cb202cfd9f 100644 --- a/subprojects/bilge-0.2-rs.wrap +++ b/subprojects/bilge-0.2-rs.wrap @@ -3,7 +3,7 @@ directory = bilge-0.2.0 source_url = https://crates.io/api/v1/crates/bilge/0.2.0/download source_filename = bilge-0.2.0.tar.gz source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57 -#method = cargo +method = cargo patch_directory = bilge-0.2-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap index 4f84eca1cc..b3dbfafb26 100644 --- a/subprojects/bilge-impl-0.2-rs.wrap +++ b/subprojects/bilge-impl-0.2-rs.wrap @@ -3,7 +3,7 @@ directory = bilge-impl-0.2.0 source_url = https://crates.io/api/v1/crates/bilge-impl/0.2.0/download source_filename = bilge-impl-0.2.0.tar.gz source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8 -#method = cargo +method = cargo patch_directory = bilge-impl-0.2-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/either-1-rs.wrap b/subprojects/either-1-rs.wrap index 352e11cfee..1c74a95645 100644 --- a/subprojects/either-1-rs.wrap +++ b/subprojects/either-1-rs.wrap @@ -3,7 +3,7 @@ directory = either-1.12.0 source_url = https://crates.io/api/v1/crates/either/1.12.0/download source_filename = either-1.12.0.tar.gz source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b -#method = cargo +method = cargo patch_directory = either-1-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/foreign-0.3-rs.wrap b/subprojects/foreign-0.3-rs.wrap index 0d218ec2c2..a5fec6c09d 100644 --- a/subprojects/foreign-0.3-rs.wrap +++ b/subprojects/foreign-0.3-rs.wrap @@ -3,5 +3,5 @@ directory = foreign-0.3.1 source_url = https://crates.io/api/v1/crates/foreign/0.3.1/download source_filename = foreign-0.3.1.tar.gz source_hash = 17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846 -#method = cargo +method = cargo patch_directory = foreign-0.3-rs diff --git a/subprojects/glib-sys-0.21-rs.wrap b/subprojects/glib-sys-0.21-rs.wrap index 313ced731a..f4e1514af5 100644 --- a/subprojects/glib-sys-0.21-rs.wrap +++ b/subprojects/glib-sys-0.21-rs.wrap @@ -3,5 +3,5 @@ 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 +method = cargo patch_directory = glib-sys-0.21-rs diff --git a/subprojects/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap index ee12d0053b..6fe89d17ca 100644 --- a/subprojects/itertools-0.11-rs.wrap +++ b/subprojects/itertools-0.11-rs.wrap @@ -3,7 +3,7 @@ directory = itertools-0.11.0 source_url = https://crates.io/api/v1/crates/itertools/0.11.0/download source_filename = itertools-0.11.0.tar.gz source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57 -#method = cargo +method = cargo patch_directory = itertools-0.11-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/libc-0.2-rs.wrap b/subprojects/libc-0.2-rs.wrap index bbe08f8788..488330178e 100644 --- a/subprojects/libc-0.2-rs.wrap +++ b/subprojects/libc-0.2-rs.wrap @@ -3,5 +3,5 @@ directory = libc-0.2.162 source_url = https://crates.io/api/v1/crates/libc/0.2.162/download source_filename = libc-0.2.162.tar.gz source_hash = 18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398 -#method = cargo +method = cargo patch_directory = libc-0.2-rs diff --git a/subprojects/probe-0.5-rs.wrap b/subprojects/probe-0.5-rs.wrap index 73229ee1c2..d3c6b18724 100644 --- a/subprojects/probe-0.5-rs.wrap +++ b/subprojects/probe-0.5-rs.wrap @@ -3,5 +3,5 @@ directory = probe-0.5.2 source_url = https://crates.io/api/v1/crates/probe/0.5.2/download source_filename = probe-0.5.2.tar.gz source_hash = 136558b6e1ebaecc92755d0ffaf9421f519531bed30cc2ad23b22cb00965cc5e -#method = cargo +method = cargo patch_directory = probe-0.5-rs diff --git a/subprojects/proc-macro-error-1-rs.wrap b/subprojects/proc-macro-error-1-rs.wrap index 59f892f782..4f300c3ac4 100644 --- a/subprojects/proc-macro-error-1-rs.wrap +++ b/subprojects/proc-macro-error-1-rs.wrap @@ -3,7 +3,7 @@ directory = proc-macro-error-1.0.4 source_url = https://crates.io/api/v1/crates/proc-macro-error/1.0.4/download source_filename = proc-macro-error-1.0.4.tar.gz source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c -#method = cargo +method = cargo patch_directory = proc-macro-error-1-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/proc-macro-error-attr-1-rs.wrap b/subprojects/proc-macro-error-attr-1-rs.wrap index 5aeb224a10..739e451c37 100644 --- a/subprojects/proc-macro-error-attr-1-rs.wrap +++ b/subprojects/proc-macro-error-attr-1-rs.wrap @@ -3,7 +3,7 @@ directory = proc-macro-error-attr-1.0.4 source_url = https://crates.io/api/v1/crates/proc-macro-error-attr/1.0.4/download source_filename = proc-macro-error-attr-1.0.4.tar.gz source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869 -#method = cargo +method = cargo patch_directory = proc-macro-error-attr-1-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 0f06cd8e11..3600f4eb19 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -3,7 +3,7 @@ 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 +method = cargo patch_directory = proc-macro2-1-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/quote-1-rs.wrap b/subprojects/quote-1-rs.wrap index 8b721dfa00..10626be652 100644 --- a/subprojects/quote-1-rs.wrap +++ b/subprojects/quote-1-rs.wrap @@ -3,7 +3,7 @@ directory = quote-1.0.36 source_url = https://crates.io/api/v1/crates/quote/1.0.36/download source_filename = quote-1.0.36.0.tar.gz source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7 -#method = cargo +method = cargo patch_directory = quote-1-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index 1e5e9d9fb6..db6576f581 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -3,7 +3,7 @@ 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 +method = cargo patch_directory = syn-2-rs # bump this version number on every change to meson.build or the patches: diff --git a/subprojects/unicode-ident-1-rs.wrap b/subprojects/unicode-ident-1-rs.wrap index 50988f612e..b420ebfe1f 100644 --- a/subprojects/unicode-ident-1-rs.wrap +++ b/subprojects/unicode-ident-1-rs.wrap @@ -3,7 +3,7 @@ directory = unicode-ident-1.0.12 source_url = https://crates.io/api/v1/crates/unicode-ident/1.0.12/download source_filename = unicode-ident-1.0.12.tar.gz source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b -#method = cargo +method = cargo patch_directory = unicode-ident-1-rs # bump this version number on every change to meson.build or the patches: From 530a9eb2c6aeae6c3b48820b47436389705a8cb1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 17:35:29 +0200 Subject: [PATCH 642/860] rust: switch to autogenerated meson rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Meson can automatically generate most of the build rules for subprojects, based on the contents of their Cargo.toml. Handwritten snippets can be placed in meson/meson.build to replace build.rs. Disable Meson's use of the nightly-only option "--env-set". It is buggy and anyway it should not be there in future version. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- meson.build | 3 +- scripts/get-wraps-from-cargo-registry.py | 6 --- subprojects/anyhow-1-rs.wrap | 3 ++ subprojects/arbitrary-int-1-rs.wrap | 1 - subprojects/attrs-0.2-rs.wrap | 1 - subprojects/bilge-0.2-rs.wrap | 1 - subprojects/bilge-impl-0.2-rs.wrap | 1 - subprojects/either-1-rs.wrap | 1 - subprojects/foreign-0.3-rs.wrap | 1 - subprojects/glib-sys-0.21-rs.wrap | 1 - subprojects/itertools-0.11-rs.wrap | 1 - subprojects/libc-0.2-rs.wrap | 3 ++ .../packagefiles/anyhow-1-rs/meson.build | 33 -------------- .../anyhow-1-rs/meson/meson.build | 9 ++++ .../arbitrary-int-1-rs/meson.build | 21 --------- .../packagefiles/attrs-0.2-rs/meson.build | 33 -------------- .../packagefiles/bilge-0.2-rs/meson.build | 31 ------------- .../bilge-impl-0.2-rs/meson.build | 44 ------------------- .../packagefiles/either-1-rs/meson.build | 26 ----------- .../packagefiles/foreign-0.3-rs/meson.build | 26 ----------- .../packagefiles/glib-sys-0.21-rs/meson.build | 33 -------------- .../itertools-0.11-rs/meson.build | 32 -------------- .../packagefiles/libc-0.2-rs/meson.build | 37 ---------------- .../libc-0.2-rs/meson/meson.build | 16 +++++++ .../packagefiles/probe-0.5-rs/meson.build | 22 ---------- .../proc-macro-error-1-rs/meson.build | 41 ----------------- .../proc-macro-error-1-rs/meson/meson.build | 3 ++ .../proc-macro-error-attr-1-rs/meson.build | 31 ------------- .../packagefiles/proc-macro2-1-rs/meson.build | 35 --------------- .../proc-macro2-1-rs/meson/meson.build | 6 +++ .../packagefiles/quote-1-rs/meson.build | 31 ------------- subprojects/packagefiles/syn-2-rs/meson.build | 43 ------------------ .../unicode-ident-1-rs/meson.build | 22 ---------- subprojects/probe-0.5-rs.wrap | 1 - subprojects/proc-macro-error-1-rs.wrap | 2 +- subprojects/proc-macro-error-attr-1-rs.wrap | 1 - subprojects/proc-macro2-1-rs.wrap | 2 +- subprojects/quote-1-rs.wrap | 1 - subprojects/syn-2-rs.wrap | 1 - subprojects/unicode-ident-1-rs.wrap | 1 - 40 files changed, 44 insertions(+), 563 deletions(-) delete mode 100644 subprojects/packagefiles/anyhow-1-rs/meson.build create mode 100644 subprojects/packagefiles/anyhow-1-rs/meson/meson.build delete mode 100644 subprojects/packagefiles/arbitrary-int-1-rs/meson.build delete mode 100644 subprojects/packagefiles/attrs-0.2-rs/meson.build delete mode 100644 subprojects/packagefiles/bilge-0.2-rs/meson.build delete mode 100644 subprojects/packagefiles/bilge-impl-0.2-rs/meson.build delete mode 100644 subprojects/packagefiles/either-1-rs/meson.build delete mode 100644 subprojects/packagefiles/foreign-0.3-rs/meson.build delete mode 100644 subprojects/packagefiles/glib-sys-0.21-rs/meson.build delete mode 100644 subprojects/packagefiles/itertools-0.11-rs/meson.build delete mode 100644 subprojects/packagefiles/libc-0.2-rs/meson.build create mode 100644 subprojects/packagefiles/libc-0.2-rs/meson/meson.build delete mode 100644 subprojects/packagefiles/probe-0.5-rs/meson.build delete mode 100644 subprojects/packagefiles/proc-macro-error-1-rs/meson.build create mode 100644 subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build delete mode 100644 subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build delete mode 100644 subprojects/packagefiles/proc-macro2-1-rs/meson.build create mode 100644 subprojects/packagefiles/proc-macro2-1-rs/meson/meson.build delete mode 100644 subprojects/packagefiles/quote-1-rs/meson.build delete mode 100644 subprojects/packagefiles/syn-2-rs/meson.build delete mode 100644 subprojects/packagefiles/unicode-ident-1-rs/meson.build diff --git a/meson.build b/meson.build index 2a289960ee..2139dfc7ce 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,8 @@ project('qemu', ['c'], meson_version: '>=1.5.0', # build.rust_std breaks with older meson, but Rust does not # support old meson anyway - (meson.version().version_compare('>= 1.9') ? ['rust_std=2021', 'build.rust_std=2021'] : []), + (meson.version().version_compare('>= 1.9') ? ['rust_std=2021', 'build.rust_std=2021', + 'rust_nightly=disabled'] : []), version: files('VERSION')) diff --git a/scripts/get-wraps-from-cargo-registry.py b/scripts/get-wraps-from-cargo-registry.py index 31eed5c2dd..78b6f1f7ae 100755 --- a/scripts/get-wraps-from-cargo-registry.py +++ b/scripts/get-wraps-from-cargo-registry.py @@ -116,12 +116,6 @@ class UpdateSubprojects: 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], diff --git a/subprojects/anyhow-1-rs.wrap b/subprojects/anyhow-1-rs.wrap index fb4603e74c..dbb28f4133 100644 --- a/subprojects/anyhow-1-rs.wrap +++ b/subprojects/anyhow-1-rs.wrap @@ -5,3 +5,6 @@ source_filename = anyhow-1.0.98.tar.gz source_hash = e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487 method = cargo patch_directory = anyhow-1-rs + +# bump this version number on every change to meson.build or the patches: +# v1 diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap index f8d7c68da6..5cf4b11e4a 100644 --- a/subprojects/arbitrary-int-1-rs.wrap +++ b/subprojects/arbitrary-int-1-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/arbitrary-int/1.2.7/download source_filename = arbitrary-int-1.2.7.tar.gz source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d method = cargo -patch_directory = arbitrary-int-1-rs # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/attrs-0.2-rs.wrap b/subprojects/attrs-0.2-rs.wrap index 4ff4c424e6..fef89ea936 100644 --- a/subprojects/attrs-0.2-rs.wrap +++ b/subprojects/attrs-0.2-rs.wrap @@ -4,4 +4,3 @@ 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/bilge-0.2-rs.wrap b/subprojects/bilge-0.2-rs.wrap index cb202cfd9f..cff10f6710 100644 --- a/subprojects/bilge-0.2-rs.wrap +++ b/subprojects/bilge-0.2-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/bilge/0.2.0/download source_filename = bilge-0.2.0.tar.gz source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57 method = cargo -patch_directory = bilge-0.2-rs # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap index b3dbfafb26..f8963eba73 100644 --- a/subprojects/bilge-impl-0.2-rs.wrap +++ b/subprojects/bilge-impl-0.2-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/bilge-impl/0.2.0/download source_filename = bilge-impl-0.2.0.tar.gz source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8 method = cargo -patch_directory = bilge-impl-0.2-rs # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/either-1-rs.wrap b/subprojects/either-1-rs.wrap index 1c74a95645..51c97df153 100644 --- a/subprojects/either-1-rs.wrap +++ b/subprojects/either-1-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/either/1.12.0/download source_filename = either-1.12.0.tar.gz source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b method = cargo -patch_directory = either-1-rs # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/foreign-0.3-rs.wrap b/subprojects/foreign-0.3-rs.wrap index a5fec6c09d..95d450397e 100644 --- a/subprojects/foreign-0.3-rs.wrap +++ b/subprojects/foreign-0.3-rs.wrap @@ -4,4 +4,3 @@ source_url = https://crates.io/api/v1/crates/foreign/0.3.1/download source_filename = foreign-0.3.1.tar.gz source_hash = 17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846 method = cargo -patch_directory = foreign-0.3-rs diff --git a/subprojects/glib-sys-0.21-rs.wrap b/subprojects/glib-sys-0.21-rs.wrap index f4e1514af5..ea4d5f66a5 100644 --- a/subprojects/glib-sys-0.21-rs.wrap +++ b/subprojects/glib-sys-0.21-rs.wrap @@ -4,4 +4,3 @@ 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/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap index 6fe89d17ca..e123ab389a 100644 --- a/subprojects/itertools-0.11-rs.wrap +++ b/subprojects/itertools-0.11-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/itertools/0.11.0/download source_filename = itertools-0.11.0.tar.gz source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57 method = cargo -patch_directory = itertools-0.11-rs # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/libc-0.2-rs.wrap b/subprojects/libc-0.2-rs.wrap index 488330178e..6e22557979 100644 --- a/subprojects/libc-0.2-rs.wrap +++ b/subprojects/libc-0.2-rs.wrap @@ -5,3 +5,6 @@ source_filename = libc-0.2.162.tar.gz source_hash = 18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398 method = cargo patch_directory = libc-0.2-rs + +# bump this version number on every change to meson.build or the patches: +# v1 diff --git a/subprojects/packagefiles/anyhow-1-rs/meson.build b/subprojects/packagefiles/anyhow-1-rs/meson.build deleted file mode 100644 index 348bab98b9..0000000000 --- a/subprojects/packagefiles/anyhow-1-rs/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -project('anyhow-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.98', - license: 'MIT OR Apache-2.0', - default_options: []) - -rustc = meson.get_compiler('rust') - -rust_args = ['--cap-lints', 'allow'] -rust_args += ['--cfg', 'feature="std"'] -if rustc.version().version_compare('<1.65.0') - error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.65.0') -endif -rust_args += [ '--cfg', 'std_backtrace' ] # >= 1.65.0 -if rustc.version().version_compare('<1.81.0') - rust_args += [ '--cfg', 'anyhow_no_core_error' ] -endif - -_anyhow_rs = static_library( - 'anyhow', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: rust_args, - dependencies: [], -) - -anyhow_dep = declare_dependency( - link_with: _anyhow_rs, -) - -meson.override_dependency('anyhow-1-rs', anyhow_dep) diff --git a/subprojects/packagefiles/anyhow-1-rs/meson/meson.build b/subprojects/packagefiles/anyhow-1-rs/meson/meson.build new file mode 100644 index 0000000000..91c46539c3 --- /dev/null +++ b/subprojects/packagefiles/anyhow-1-rs/meson/meson.build @@ -0,0 +1,9 @@ +rustc = meson.get_compiler('rust') + +if rustc.version().version_compare('<1.65.0') + error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.65.0') +endif +extra_args += [ '--cfg', 'std_backtrace' ] # >= 1.65.0 +if rustc.version().version_compare('<1.81.0') + extra_args += [ '--cfg', 'anyhow_no_core_error' ] +endif diff --git a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build b/subprojects/packagefiles/arbitrary-int-1-rs/meson.build deleted file mode 100644 index 00733d1faa..0000000000 --- a/subprojects/packagefiles/arbitrary-int-1-rs/meson.build +++ /dev/null @@ -1,21 +0,0 @@ -project('arbitrary-int-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.2.7', - license: 'MIT', - default_options: []) - -_arbitrary_int_rs = static_library( - 'arbitrary_int', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: ['--cap-lints', 'allow'], - rust_abi: 'rust', - dependencies: [], -) - -arbitrary_int_dep = declare_dependency( - link_with: _arbitrary_int_rs, -) - -meson.override_dependency('arbitrary-int-1-rs', arbitrary_int_dep) diff --git a/subprojects/packagefiles/attrs-0.2-rs/meson.build b/subprojects/packagefiles/attrs-0.2-rs/meson.build deleted file mode 100644 index ee575476cb..0000000000 --- a/subprojects/packagefiles/attrs-0.2-rs/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -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) diff --git a/subprojects/packagefiles/bilge-0.2-rs/meson.build b/subprojects/packagefiles/bilge-0.2-rs/meson.build deleted file mode 100644 index ce13d0fe80..0000000000 --- a/subprojects/packagefiles/bilge-0.2-rs/meson.build +++ /dev/null @@ -1,31 +0,0 @@ -project( - 'bilge-0.2-rs', - 'rust', - meson_version: '>=1.5.0', - version : '0.2.0', - license : 'MIT or Apache-2.0', -) - -subproject('arbitrary-int-1-rs', required: true) -subproject('bilge-impl-0.2-rs', required: true) - -arbitrary_int_dep = dependency('arbitrary-int-1-rs') -bilge_impl_dep = dependency('bilge-impl-0.2-rs') - -lib = static_library( - 'bilge', - 'src/lib.rs', - override_options : ['rust_std=2021', 'build.rust_std=2021'], - rust_abi : 'rust', - rust_args: ['--cap-lints', 'allow'], - dependencies: [ - arbitrary_int_dep, - bilge_impl_dep, - ], -) - -bilge_dep = declare_dependency( - link_with : [lib], -) - -meson.override_dependency('bilge-0.2-rs', bilge_dep) diff --git a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build b/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build deleted file mode 100644 index 04617b875c..0000000000 --- a/subprojects/packagefiles/bilge-impl-0.2-rs/meson.build +++ /dev/null @@ -1,44 +0,0 @@ -project('bilge-impl-0.2-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.2.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('itertools-0.11-rs', required: true) -subproject('proc-macro-error-attr-1-rs', required: true) -subproject('proc-macro-error-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('syn-2-rs', required: true) -subproject('proc-macro2-1-rs', required: true) - -itertools_dep = dependency('itertools-0.11-rs', native: true) -proc_macro_error_attr_dep = dependency('proc-macro-error-attr-1-rs', native: true) -proc_macro_error_dep = dependency('proc-macro-error-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) -syn_dep = dependency('syn-2-rs', native: true) -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) - -rust = import('rust') - -_bilge_impl_rs = rust.proc_macro( - 'bilge_impl', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_args: [ - '--cap-lints', 'allow', - ], - dependencies: [ - itertools_dep, - proc_macro_error_attr_dep, - proc_macro_error_dep, - quote_dep, - syn_dep, - proc_macro2_dep, - ], -) - -bilge_impl_dep = declare_dependency( - link_with: _bilge_impl_rs, -) - -meson.override_dependency('bilge-impl-0.2-rs', bilge_impl_dep) diff --git a/subprojects/packagefiles/either-1-rs/meson.build b/subprojects/packagefiles/either-1-rs/meson.build deleted file mode 100644 index 04c96cc5fb..0000000000 --- a/subprojects/packagefiles/either-1-rs/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -project('either-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.12.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -_either_rs = static_library( - 'either', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="use_std"', - '--cfg', 'feature="use_alloc"', - ], - dependencies: [], - native: true, -) - -either_dep = declare_dependency( - link_with: _either_rs, -) - -meson.override_dependency('either-1-rs', either_dep, native: true) diff --git a/subprojects/packagefiles/foreign-0.3-rs/meson.build b/subprojects/packagefiles/foreign-0.3-rs/meson.build deleted file mode 100644 index 0901c02c52..0000000000 --- a/subprojects/packagefiles/foreign-0.3-rs/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -project('foreign-0.3-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.2.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('libc-0.2-rs', required: true) -libc_rs = dependency('libc-0.2-rs') - -_foreign_rs = static_library( - 'foreign', - 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: [libc_rs], -) - -foreign_dep = declare_dependency( - link_with: _foreign_rs, -) - -meson.override_dependency('foreign-0.3-rs', foreign_dep) diff --git a/subprojects/packagefiles/glib-sys-0.21-rs/meson.build b/subprojects/packagefiles/glib-sys-0.21-rs/meson.build deleted file mode 100644 index 8c5483311e..0000000000 --- a/subprojects/packagefiles/glib-sys-0.21-rs/meson.build +++ /dev/null @@ -1,33 +0,0 @@ -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) diff --git a/subprojects/packagefiles/itertools-0.11-rs/meson.build b/subprojects/packagefiles/itertools-0.11-rs/meson.build deleted file mode 100644 index 2a3fbe9ee5..0000000000 --- a/subprojects/packagefiles/itertools-0.11-rs/meson.build +++ /dev/null @@ -1,32 +0,0 @@ -project('itertools-0.11-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.11.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('either-1-rs', required: true) - -either_dep = dependency('either-1-rs', native: true) - -_itertools_rs = static_library( - 'itertools', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'feature="use_std"', - '--cfg', 'feature="use_alloc"', - ], - dependencies: [ - either_dep, - ], - native: true, -) - -itertools_dep = declare_dependency( - link_with: _itertools_rs, -) - -meson.override_dependency('itertools-0.11-rs', itertools_dep, native: true) diff --git a/subprojects/packagefiles/libc-0.2-rs/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson.build deleted file mode 100644 index ac4f80dba9..0000000000 --- a/subprojects/packagefiles/libc-0.2-rs/meson.build +++ /dev/null @@ -1,37 +0,0 @@ -project('libc-0.2-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.2.162', - license: 'MIT OR Apache-2.0', - default_options: []) - -_libc_rs = static_library( - 'libc', - files('src/lib.rs'), - gnu_symbol_visibility: 'hidden', - override_options: ['rust_std=2015', 'build.rust_std=2015'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'freebsd11', - '--cfg', 'libc_priv_mod_use', - '--cfg', 'libc_union', - '--cfg', 'libc_const_size_of', - '--cfg', 'libc_align', - '--cfg', 'libc_int128', - '--cfg', 'libc_core_cvoid', - '--cfg', 'libc_packedN', - '--cfg', 'libc_cfg_target_vendor', - '--cfg', 'libc_non_exhaustive', - '--cfg', 'libc_long_array', - '--cfg', 'libc_ptr_addr_of', - '--cfg', 'libc_underscore_const_names', - '--cfg', 'libc_const_extern_fn', - ], - dependencies: [], -) - -libc_dep = declare_dependency( - link_with: _libc_rs, -) - -meson.override_dependency('libc-0.2-rs', libc_dep) diff --git a/subprojects/packagefiles/libc-0.2-rs/meson/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson/meson.build new file mode 100644 index 0000000000..db9480ada1 --- /dev/null +++ b/subprojects/packagefiles/libc-0.2-rs/meson/meson.build @@ -0,0 +1,16 @@ +extra_args += [ + '--cfg', 'freebsd11', + '--cfg', 'libc_priv_mod_use', + '--cfg', 'libc_union', + '--cfg', 'libc_const_size_of', + '--cfg', 'libc_align', + '--cfg', 'libc_int128', + '--cfg', 'libc_core_cvoid', + '--cfg', 'libc_packedN', + '--cfg', 'libc_cfg_target_vendor', + '--cfg', 'libc_non_exhaustive', + '--cfg', 'libc_long_array', + '--cfg', 'libc_ptr_addr_of', + '--cfg', 'libc_underscore_const_names', + '--cfg', 'libc_const_extern_fn', +] diff --git a/subprojects/packagefiles/probe-0.5-rs/meson.build b/subprojects/packagefiles/probe-0.5-rs/meson.build deleted file mode 100644 index e6ea69533b..0000000000 --- a/subprojects/packagefiles/probe-0.5-rs/meson.build +++ /dev/null @@ -1,22 +0,0 @@ -project('probe-0.5-rs', 'rust', - meson_version: '>=1.5.0', - version: '0.5.2', - license: 'Apache-2.0 OR MIT', - default_options: []) - -_probe_rs = static_library( - 'probe', - 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', - ], -) - -probe_deps = declare_dependency( - link_with: _probe_rs, -) - -meson.override_dependency('probe-0.5-rs', probe_deps) diff --git a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-1-rs/meson.build deleted file mode 100644 index 8ba558e133..0000000000 --- a/subprojects/packagefiles/proc-macro-error-1-rs/meson.build +++ /dev/null @@ -1,41 +0,0 @@ -project('proc-macro-error-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.4', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro-error-attr-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('syn-2-rs', required: true) -subproject('proc-macro2-1-rs', required: true) - -proc_macro_error_attr_dep = dependency('proc-macro-error-attr-1-rs', native: true) -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) -syn_dep = dependency('syn-2-rs', native: true) - -_proc_macro_error_rs = static_library( - 'proc_macro_error', - files('src/lib.rs'), - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_abi: 'rust', - rust_args: [ - '--cap-lints', 'allow', - '--cfg', 'use_fallback', - '--cfg', 'feature="syn-error"', - '-A', 'non_fmt_panics' - ], - dependencies: [ - proc_macro_error_attr_dep, - proc_macro2_dep, - quote_dep, - syn_dep, - ], - native: true, -) - -proc_macro_error_dep = declare_dependency( - link_with: _proc_macro_error_rs, -) - -meson.override_dependency('proc-macro-error-1-rs', proc_macro_error_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build b/subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build new file mode 100644 index 0000000000..52a244d2b1 --- /dev/null +++ b/subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build @@ -0,0 +1,3 @@ +extra_args = [ + '--cfg', 'use_fallback', +] diff --git a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build b/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build deleted file mode 100644 index a85d7c0714..0000000000 --- a/subprojects/packagefiles/proc-macro-error-attr-1-rs/meson.build +++ /dev/null @@ -1,31 +0,0 @@ -project('proc-macro-error-attr-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.12.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro2-1-rs', required: true) -subproject('quote-1-rs', required: true) - -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) - -rust = import('rust') -_proc_macro_error_attr_rs = rust.proc_macro( - 'proc_macro_error_attr', - files('src/lib.rs'), - override_options: ['rust_std=2018', 'build.rust_std=2018'], - rust_args: [ - '--cap-lints', 'allow', - ], - dependencies: [ - proc_macro2_dep, - quote_dep, - ], -) - -proc_macro_error_attr_dep = declare_dependency( - link_with: _proc_macro_error_attr_rs, -) - -meson.override_dependency('proc-macro-error-attr-1-rs', proc_macro_error_attr_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build deleted file mode 100644 index ba7de07029..0000000000 --- a/subprojects/packagefiles/proc-macro2-1-rs/meson.build +++ /dev/null @@ -1,35 +0,0 @@ -project('proc-macro2-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.95', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('unicode-ident-1-rs', required: true) - -unicode_ident_dep = dependency('unicode-ident-1-rs', native: true) - -_proc_macro2_rs = static_library( - 'proc_macro2', - 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="proc-macro"', - '--cfg', 'no_literal_byte_character', - '--cfg', 'no_literal_c_string', - '--cfg', 'no_source_text', - '--cfg', 'wrap_proc_macro', - ], - dependencies: [ - unicode_ident_dep, - ], - native: true, -) - -proc_macro2_dep = declare_dependency( - link_with: _proc_macro2_rs, -) - -meson.override_dependency('proc-macro2-1-rs', proc_macro2_dep, native: true) diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson/meson.build new file mode 100644 index 0000000000..09712bb0b0 --- /dev/null +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson/meson.build @@ -0,0 +1,6 @@ +extra_args = [ + '--cfg', 'no_literal_byte_character', + '--cfg', 'no_literal_c_string', + '--cfg', 'no_source_text', + '--cfg', 'wrap_proc_macro', +] diff --git a/subprojects/packagefiles/quote-1-rs/meson.build b/subprojects/packagefiles/quote-1-rs/meson.build deleted file mode 100644 index bf41fad99b..0000000000 --- a/subprojects/packagefiles/quote-1-rs/meson.build +++ /dev/null @@ -1,31 +0,0 @@ -project('quote-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.12.0', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro2-1-rs', required: true) - -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) - -_quote_rs = static_library( - 'quote', - 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="proc-macro"', - ], - dependencies: [ - proc_macro2_dep, - ], - native: true, -) - -quote_dep = declare_dependency( - link_with: _quote_rs, -) - -meson.override_dependency('quote-1-rs', quote_dep, native: true) diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build deleted file mode 100644 index 3e6dc318a9..0000000000 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ /dev/null @@ -1,43 +0,0 @@ -project('syn-2-rs', 'rust', - meson_version: '>=1.5.0', - version: '2.0.104', - license: 'MIT OR Apache-2.0', - default_options: []) - -subproject('proc-macro2-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('unicode-ident-1-rs', required: true) - -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) -quote_dep = dependency('quote-1-rs', native: true) -unicode_ident_dep = dependency('unicode-ident-1-rs', native: true) - -_syn_rs = static_library( - 'syn', - 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="full"', - '--cfg', 'feature="derive"', - '--cfg', 'feature="parsing"', - '--cfg', 'feature="printing"', - '--cfg', 'feature="clone-impls"', - '--cfg', 'feature="proc-macro"', - '--cfg', 'feature="extra-traits"', - ], - dependencies: [ - quote_dep, - proc_macro2_dep, - unicode_ident_dep, - ], - native: true, -) - -syn_dep = declare_dependency( - link_with: _syn_rs, -) - -meson.override_dependency('syn-2-rs', syn_dep, native: true) diff --git a/subprojects/packagefiles/unicode-ident-1-rs/meson.build b/subprojects/packagefiles/unicode-ident-1-rs/meson.build deleted file mode 100644 index 11a5dab97d..0000000000 --- a/subprojects/packagefiles/unicode-ident-1-rs/meson.build +++ /dev/null @@ -1,22 +0,0 @@ -project('unicode-ident-1-rs', 'rust', - meson_version: '>=1.5.0', - version: '1.0.12', - license: '(MIT OR Apache-2.0) AND Unicode-DFS-2016', - default_options: []) - -_unicode_ident_rs = static_library( - 'unicode_ident', - 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: [], - native: true, -) - -unicode_ident_dep = declare_dependency( - link_with: _unicode_ident_rs, -) - -meson.override_dependency('unicode-ident-1-rs', unicode_ident_dep, native: true) diff --git a/subprojects/probe-0.5-rs.wrap b/subprojects/probe-0.5-rs.wrap index d3c6b18724..72e771ec78 100644 --- a/subprojects/probe-0.5-rs.wrap +++ b/subprojects/probe-0.5-rs.wrap @@ -4,4 +4,3 @@ source_url = https://crates.io/api/v1/crates/probe/0.5.2/download source_filename = probe-0.5.2.tar.gz source_hash = 136558b6e1ebaecc92755d0ffaf9421f519531bed30cc2ad23b22cb00965cc5e method = cargo -patch_directory = probe-0.5-rs diff --git a/subprojects/proc-macro-error-1-rs.wrap b/subprojects/proc-macro-error-1-rs.wrap index 4f300c3ac4..e57921bfad 100644 --- a/subprojects/proc-macro-error-1-rs.wrap +++ b/subprojects/proc-macro-error-1-rs.wrap @@ -7,4 +7,4 @@ method = cargo patch_directory = proc-macro-error-1-rs # bump this version number on every change to meson.build or the patches: -# v2 +# v3 diff --git a/subprojects/proc-macro-error-attr-1-rs.wrap b/subprojects/proc-macro-error-attr-1-rs.wrap index 739e451c37..456d2c2b6d 100644 --- a/subprojects/proc-macro-error-attr-1-rs.wrap +++ b/subprojects/proc-macro-error-attr-1-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/proc-macro-error-attr/1.0.4/downloa source_filename = proc-macro-error-attr-1.0.4.tar.gz source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869 method = cargo -patch_directory = proc-macro-error-attr-1-rs # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 3600f4eb19..676d1a3a8c 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -7,4 +7,4 @@ method = cargo patch_directory = proc-macro2-1-rs # bump this version number on every change to meson.build or the patches: -# v2 +# v3 diff --git a/subprojects/quote-1-rs.wrap b/subprojects/quote-1-rs.wrap index 10626be652..5f38f56499 100644 --- a/subprojects/quote-1-rs.wrap +++ b/subprojects/quote-1-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/quote/1.0.36/download source_filename = quote-1.0.36.0.tar.gz source_hash = 0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7 method = cargo -patch_directory = quote-1-rs # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index db6576f581..4b97bed258 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -4,7 +4,6 @@ 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 # bump this version number on every change to meson.build or the patches: # v2 diff --git a/subprojects/unicode-ident-1-rs.wrap b/subprojects/unicode-ident-1-rs.wrap index b420ebfe1f..90aae02ba0 100644 --- a/subprojects/unicode-ident-1-rs.wrap +++ b/subprojects/unicode-ident-1-rs.wrap @@ -4,7 +4,6 @@ source_url = https://crates.io/api/v1/crates/unicode-ident/1.0.12/download source_filename = unicode-ident-1.0.12.tar.gz source_hash = 3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b method = cargo -patch_directory = unicode-ident-1-rs # bump this version number on every change to meson.build or the patches: # v2 From aaac1f8229efaf1bd13ccdff1a88af142aebafe9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 29 Apr 2026 10:36:52 +0200 Subject: [PATCH 643/860] meson: use compiler_target() to get rustc target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new method in Meson 1.11.x removes the need to pass the triple via config-host.mak. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- configure | 3 --- meson.build | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/configure b/configure index 7f637c018c..56158cd95f 100755 --- a/configure +++ b/configure @@ -1740,9 +1740,6 @@ if test "$container_command" != ""; then echo "CONTAINER_COMMAND=$container_command" >> $config_host_mak fi echo "SUBDIRS=$subdirs" >> $config_host_mak -if test "$rust" != disabled; then - echo "RUST_TARGET_TRIPLE=$rust_target_triple" >> $config_host_mak -fi echo "PYTHON=$python" >> $config_host_mak echo "MKVENV_ENSUREGROUP=$mkvenv ensuregroup $mkvenv_online_flag" >> $config_host_mak echo "GENISOIMAGE=$genisoimage" >> $config_host_mak diff --git a/meson.build b/meson.build index 2139dfc7ce..2593eb9762 100644 --- a/meson.build +++ b/meson.build @@ -4747,7 +4747,7 @@ else endif summary_info += {'Rust support': have_rust} if have_rust - summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']} + summary_info += {'Rust target': rust.compiler_target(native: false)} summary_info += {'rustc': ' '.join(rustc.cmd_array())} summary_info += {'rustc version': rustc.version()} summary_info += {'rustdoc': rustdoc} From 4c5c26548b746e5866b2c00c353f43e6e322ad96 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 17:47:55 +0200 Subject: [PATCH 644/860] rust: use meson automatic parsing of Cargo.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finally, automatic parsing of Cargo.toml is also possible for QEMU's own crates, not just for subprojects. This removes the need to list the dependencies and language editions in both Cargo.toml and meson.build files. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- meson.build | 7 +------ rust/bindings/chardev-sys/meson.build | 11 ++++------- rust/bindings/hwcore-sys/meson.build | 11 ++++------- rust/bindings/migration-sys/meson.build | 11 ++++------- rust/bindings/qom-sys/meson.build | 11 ++++------- rust/bindings/system-sys/meson.build | 11 ++++------- rust/bindings/util-sys/meson.build | 11 ++++------- rust/bits/meson.build | 7 ++----- rust/bql/meson.build | 13 +++---------- rust/chardev/meson.build | 8 ++------ rust/common/meson.build | 10 +++------- rust/hw/char/pl011/meson.build | 22 +--------------------- rust/hw/core/meson.build | 10 ++-------- rust/hw/timer/hpet/meson.build | 17 +---------------- rust/meson.build | 11 ----------- rust/migration/meson.build | 8 ++------ rust/qemu-macros/meson.build | 12 ++---------- rust/qom/meson.build | 8 ++------ rust/system/meson.build | 8 ++------ rust/trace/meson.build | 9 +++------ rust/util/meson.build | 7 ++----- 21 files changed, 52 insertions(+), 171 deletions(-) diff --git a/meson.build b/meson.build index 2593eb9762..3c6a82ab44 100644 --- a/meson.build +++ b/meson.build @@ -135,12 +135,7 @@ if have_rust rustfmt = find_program('rustfmt', required: false) - rustc_lint_args = run_command(rustc_args, '--lints', - capture: true, check: true).stdout().strip().splitlines() - if get_option('strict_rust_lints') - rustc_lint_args += ['-Dwarnings', '-Funknown_lints'] - endif - + rustc_lint_args = get_option('strict_rust_lints') ? ['-Dwarnings', '-Funknown_lints'] : [] add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'], native: false, language: 'rust') add_project_arguments(rustc_lint_args + ['--cfg', 'MESON'], diff --git a/rust/bindings/chardev-sys/meson.build b/rust/bindings/chardev-sys/meson.build index 458075b806..7af5d74dd5 100644 --- a/rust/bindings/chardev-sys/meson.build +++ b/rust/bindings/chardev-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_chardev_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['chardev-sys'].split(), kwargs: bindgen_kwargs) -_chardev_sys_rs = static_library( - 'chardev_sys', - structured_sources(['lib.rs', _bindgen_chardev_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs, common_rs, qom_sys_rs, util_sys_rs], -) + +_chardev_sys_rs = cargo_ws.package('chardev-sys').library( + structured_sources(['lib.rs', _bindgen_chardev_rs])) +cargo_ws.package('chardev-sys').override_dependency(declare_dependency(link_with: _chardev_sys_rs)) chardev_sys_rs = declare_dependency(link_with: [_chardev_sys_rs]) diff --git a/rust/bindings/hwcore-sys/meson.build b/rust/bindings/hwcore-sys/meson.build index 3d51947b4a..6b8d218f69 100644 --- a/rust/bindings/hwcore-sys/meson.build +++ b/rust/bindings/hwcore-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_hwcore_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['hwcore-sys'].split(), kwargs: bindgen_kwargs) -_hwcore_sys_rs = static_library( - 'hwcore_sys', - structured_sources(['lib.rs', _bindgen_hwcore_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [common_rs, glib_sys_rs, qom_sys_rs, util_sys_rs, migration_sys_rs, chardev_sys_rs], -) + +_hwcore_sys_rs = cargo_ws.package('hwcore-sys').library( + structured_sources(['lib.rs', _bindgen_hwcore_rs])) +cargo_ws.package('hwcore-sys').override_dependency(declare_dependency(link_with: _hwcore_sys_rs)) hwcore_sys_rs = declare_dependency(link_with: [_hwcore_sys_rs]) diff --git a/rust/bindings/migration-sys/meson.build b/rust/bindings/migration-sys/meson.build index 9243acba30..91f2967c9b 100644 --- a/rust/bindings/migration-sys/meson.build +++ b/rust/bindings/migration-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_migration_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['migration-sys'].split(), kwargs: bindgen_kwargs) -_migration_sys_rs = static_library( - 'migration_sys', - structured_sources(['lib.rs', _bindgen_migration_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs, common_rs, util_sys_rs], -) + +_migration_sys_rs = cargo_ws.package('migration-sys').library( + structured_sources(['lib.rs', _bindgen_migration_rs])) +cargo_ws.package('migration-sys').override_dependency(declare_dependency(link_with: _migration_sys_rs)) migration_sys_rs = declare_dependency(link_with: [_migration_sys_rs]) diff --git a/rust/bindings/qom-sys/meson.build b/rust/bindings/qom-sys/meson.build index 8f8ae7d1bc..540e55eea8 100644 --- a/rust/bindings/qom-sys/meson.build +++ b/rust/bindings/qom-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_qom_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['qom-sys'].split(), kwargs: bindgen_kwargs) -_qom_sys_rs = static_library( - 'qom_sys', - structured_sources(['lib.rs', _bindgen_qom_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs, util_sys_rs], -) + +_qom_sys_rs = cargo_ws.package('qom-sys').library( + structured_sources(['lib.rs', _bindgen_qom_rs])) +cargo_ws.package('qom-sys').override_dependency(declare_dependency(link_with: _qom_sys_rs)) qom_sys_rs = declare_dependency(link_with: [_qom_sys_rs]) diff --git a/rust/bindings/system-sys/meson.build b/rust/bindings/system-sys/meson.build index aa5e880114..b1f4770499 100644 --- a/rust/bindings/system-sys/meson.build +++ b/rust/bindings/system-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_system_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['system-sys'].split(), kwargs: bindgen_kwargs) -_system_sys_rs = static_library( - 'system_sys', - structured_sources(['lib.rs', _bindgen_system_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [common_rs, glib_sys_rs, hwcore_sys_rs, migration_sys_rs, qom_sys_rs, util_sys_rs], -) + +_system_sys_rs = cargo_ws.package('system-sys').library( + structured_sources(['lib.rs', _bindgen_system_rs])) +cargo_ws.package('system-sys').override_dependency(declare_dependency(link_with: _system_sys_rs)) system_sys_rs = declare_dependency(link_with: [_system_sys_rs]) diff --git a/rust/bindings/util-sys/meson.build b/rust/bindings/util-sys/meson.build index c37f50a94b..5a9db6152e 100644 --- a/rust/bindings/util-sys/meson.build +++ b/rust/bindings/util-sys/meson.build @@ -1,12 +1,9 @@ _bindgen_util_rs = rust.bindgen( args: bindgen_args_common + bindgen_args_data['util-sys'].split(), kwargs: bindgen_kwargs) -_util_sys_rs = static_library( - 'util_sys', - structured_sources(['lib.rs', _bindgen_util_rs]), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - dependencies: [glib_sys_rs], -) + +_util_sys_rs = cargo_ws.package('util-sys').library( + structured_sources(['lib.rs', _bindgen_util_rs])) +cargo_ws.package('util-sys').override_dependency(declare_dependency(link_with: _util_sys_rs)) util_sys_rs = declare_dependency(link_with: [_util_sys_rs]) diff --git a/rust/bits/meson.build b/rust/bits/meson.build index c0094ffcf3..c64a17ba15 100644 --- a/rust/bits/meson.build +++ b/rust/bits/meson.build @@ -1,8 +1,5 @@ -_bits_rs = static_library( - 'bits', - 'src/lib.rs', - dependencies: [qemu_macros], -) +_bits_rs = cargo_ws.package('bits').library() +cargo_ws.package('bits').override_dependency(declare_dependency(link_with: _bits_rs)) bits_rs = declare_dependency(link_with: _bits_rs) diff --git a/rust/bql/meson.build b/rust/bql/meson.build index de295d2983..8bdc9b53a2 100644 --- a/rust/bql/meson.build +++ b/rust/bql/meson.build @@ -1,17 +1,10 @@ -_bql_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), - capture: true, check: true).stdout().strip().splitlines() - +_bql_cfg = [] if get_option('debug_mutex') _bql_cfg += ['--cfg', 'feature="debug_cell"'] endif -_bql_rs = static_library( - 'bql', - 'src/lib.rs', - rust_args: _bql_cfg, - dependencies: [glib_sys_rs, util_sys_rs], -) +_bql_rs = cargo_ws.package('bql').library(rust_args: _bql_cfg) +cargo_ws.package('bql').override_dependency(declare_dependency(link_with: _bql_rs)) bql_rs = declare_dependency(link_with: [_bql_rs], dependencies: [qemuutil]) diff --git a/rust/chardev/meson.build b/rust/chardev/meson.build index 7b267fd23a..f3462e9209 100644 --- a/rust/chardev/meson.build +++ b/rust/chardev/meson.build @@ -1,8 +1,4 @@ -_chardev_rs = static_library( - 'chardev', - 'src/lib.rs', - link_with: [_bql_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, common_rs, qemu_macros, chardev_sys_rs], -) +_chardev_rs = cargo_ws.package('chardev').library() +cargo_ws.package('chardev').override_dependency(declare_dependency(link_with: _chardev_rs)) chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil]) diff --git a/rust/common/meson.build b/rust/common/meson.build index 57091b18fc..43afe417dc 100644 --- a/rust/common/meson.build +++ b/rust/common/meson.build @@ -1,13 +1,9 @@ _common_cfg = run_command(rustc_args, - '--config-headers', config_host_h, '--features', files('Cargo.toml'), + '--config-headers', config_host_h, files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() -_common_rs = static_library( - 'common', - 'src/lib.rs', - rust_args: _common_cfg, - dependencies: [libc_rs, qemu_macros], -) +_common_rs = cargo_ws.package('common').library(rust_args: _common_cfg) +cargo_ws.package('common').override_dependency(declare_dependency(link_with: _common_rs)) common_rs = declare_dependency(link_with: [_common_rs]) diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 1a1a09e508..92b7d375f7 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -8,8 +8,7 @@ _libpl011_bindings_inc_rs = rust.bindgen( kwargs: bindgen_kwargs, ) -_libpl011_rs = static_library( - 'pl011', +_libpl011_rs = cargo_ws.package('pl011').library( structured_sources( [ 'src/lib.rs', @@ -19,25 +18,6 @@ _libpl011_rs = static_library( ], {'.' : _libpl011_bindings_inc_rs}, ), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [ - _util_rs, - _migration_rs, - _bql_rs, - _qom_rs, - _chardev_rs, - _system_rs, - _hwcore_rs, - _trace_rs - ], - dependencies: [ - bilge_rs, - bilge_impl_rs, - bits_rs, - common_rs, - glib_sys_rs, - ], ) rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build index 6d1bfd4d20..da0f13d314 100644 --- a/rust/hw/core/meson.build +++ b/rust/hw/core/meson.build @@ -1,11 +1,5 @@ -_hwcore_rs = static_library( - 'hwcore', - 'src/lib.rs', - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [_bql_rs, _chardev_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, qemu_macros, common_rs, hwcore_sys_rs], -) +_hwcore_rs = cargo_ws.package('hwcore').library() +cargo_ws.package('hwcore').override_dependency(declare_dependency(link_with: _hwcore_rs)) hwcore_rs = declare_dependency(link_with: [_hwcore_rs], dependencies: [qom_rs, hwcore]) diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build index 3bb7ce2a6c..419e8f40b7 100644 --- a/rust/hw/timer/hpet/meson.build +++ b/rust/hw/timer/hpet/meson.build @@ -1,19 +1,4 @@ -_libhpet_rs = static_library( - 'hpet', - files('src/lib.rs'), - override_options: ['rust_std=2021', 'build.rust_std=2021'], - rust_abi: 'rust', - link_with: [ - _util_rs, - _migration_rs, - _bql_rs, - _qom_rs, - _system_rs, - _hwcore_rs, - _trace_rs, - ], - dependencies: [common_rs], -) +_libhpet_rs = cargo_ws.package('hpet').library() rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency( link_whole: [_libhpet_rs], diff --git a/rust/meson.build b/rust/meson.build index 1622a50a97..c0c4e52c1b 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -5,17 +5,6 @@ else endif cargo_ws = import('rust').workspace() -anyhow_rs = cargo_ws.subproject('anyhow').dependency() -bilge_rs = cargo_ws.subproject('bilge').dependency() -bilge_impl_rs = cargo_ws.subproject('bilge-impl').dependency() -foreign_rs = cargo_ws.subproject('foreign').dependency() -glib_sys_rs = cargo_ws.subproject('glib-sys').dependency() -libc_rs = cargo_ws.subproject('libc').dependency() -probe_rs = cargo_ws.subproject('probe').dependency() -quote_rs_native = cargo_ws.subproject('quote').dependency() -syn_rs_native = cargo_ws.subproject('syn').dependency() -proc_macro2_rs_native = cargo_ws.subproject('proc-macro2').dependency() -attrs_rs_native = cargo_ws.subproject('attrs').dependency() genrs = [] diff --git a/rust/migration/meson.build b/rust/migration/meson.build index 9626689777..b9ba82989e 100644 --- a/rust/migration/meson.build +++ b/rust/migration/meson.build @@ -1,9 +1,5 @@ -_migration_rs = static_library( - 'migration', - 'src/lib.rs', - link_with: [_util_rs, _bql_rs], - dependencies: [common_rs, glib_sys_rs, qemu_macros, migration_sys_rs], -) +_migration_rs = cargo_ws.package('migration').library() +cargo_ws.package('migration').override_dependency(declare_dependency(link_with: _migration_rs)) migration_rs = declare_dependency(link_with: [_migration_rs], dependencies: [bql_rs, migration, qemuutil]) diff --git a/rust/qemu-macros/meson.build b/rust/qemu-macros/meson.build index cdea5bf439..7b25ccefe3 100644 --- a/rust/qemu-macros/meson.build +++ b/rust/qemu-macros/meson.build @@ -1,13 +1,5 @@ -_qemu_macros_rs = rust.proc_macro( - 'qemu_macros', - files('src/lib.rs'), - dependencies: [ - attrs_rs_native, - proc_macro2_rs_native, - quote_rs_native, - syn_rs_native, - ], -) +_qemu_macros_rs = cargo_ws.package('qemu_macros').proc_macro() +cargo_ws.package('qemu_macros').override_dependency(declare_dependency(link_with: _qemu_macros_rs)) qemu_macros = declare_dependency( link_with: _qemu_macros_rs, diff --git a/rust/qom/meson.build b/rust/qom/meson.build index 9865da280c..6736cfb24c 100644 --- a/rust/qom/meson.build +++ b/rust/qom/meson.build @@ -1,9 +1,5 @@ -_qom_rs = static_library( - 'qom', - 'src/lib.rs', - link_with: [_bql_rs, _migration_rs], - dependencies: [common_rs, glib_sys_rs, qemu_macros, qom_sys_rs], -) +_qom_rs = cargo_ws.package('qom').library() +cargo_ws.package('qom').override_dependency(declare_dependency(link_with: _qom_rs)) qom_rs = declare_dependency(link_with: [_qom_rs], dependencies: [qemu_macros, qom, qemuutil]) diff --git a/rust/system/meson.build b/rust/system/meson.build index 89c1f2b84d..b1edcd6bbe 100644 --- a/rust/system/meson.build +++ b/rust/system/meson.build @@ -1,9 +1,5 @@ -_system_rs = static_library( - 'system', - 'src/lib.rs', - link_with: [_bql_rs, _hwcore_rs, _migration_rs, _qom_rs, _util_rs], - dependencies: [glib_sys_rs, common_rs, qemu_macros, system_sys_rs], -) +_system_rs = cargo_ws.package('system').library() +cargo_ws.package('system').override_dependency(declare_dependency(link_with: _system_rs)) system_rs = declare_dependency(link_with: [_system_rs], dependencies: [hwcore]) diff --git a/rust/trace/meson.build b/rust/trace/meson.build index 0071a49cf7..b049f73b54 100644 --- a/rust/trace/meson.build +++ b/rust/trace/meson.build @@ -7,11 +7,8 @@ lib_rs = configure_file( '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 - dependencies: [libc_rs, probe_rs], -) +_trace_rs = cargo_ws.package('trace').library( + structured_sources([lib_rs, trace_rs_targets])) +cargo_ws.package('trace').override_dependency(declare_dependency(link_with: _trace_rs)) trace_rs = declare_dependency(link_with: _trace_rs) diff --git a/rust/util/meson.build b/rust/util/meson.build index 6d175ae0b0..704501b573 100644 --- a/rust/util/meson.build +++ b/rust/util/meson.build @@ -1,8 +1,5 @@ -_util_rs = static_library( - 'util', - 'src/lib.rs', - dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, util_sys_rs], -) +_util_rs = cargo_ws.package('util').library() +cargo_ws.package('util').override_dependency(declare_dependency(link_with: _util_rs)) util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom]) From aac1b59b8577fc939c297025886a44d25bd635c3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Jul 2026 15:57:39 +0200 Subject: [PATCH 645/860] docs: rust: update for new-style build rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 83 +++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 56 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index af142c4a69..94c550b39b 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -21,15 +21,11 @@ invokes rustc directly, building static libraries that are then linked together with the C code. This is completely automatic when you run ``make`` or ``ninja``. -However, QEMU's build system also tries to be easy to use for people who -are accustomed to the more "normal" Cargo-based development workflow. -In particular: - -* the set of warnings and lints that are used to build QEMU always - comes from the ``Cargo.toml`` workspace file - -* it is also possible to use ``cargo`` for common Rust-specific coding - tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``. +However, Meson is able to consume ``Cargo.toml`` files and tries +to be easy to use for people who are accustomed to the more "normal" +Cargo-based development workflow. In the case of QEMU, in addition, +it is possible to use ``cargo`` for common Rust-specific coding +tasks, in particular to invoke ``clippy``, ``rustfmt`` and ``rustdoc``. To this end, QEMU includes a ``build.rs`` build script that picks up generated sources from QEMU's build directory and puts it in Cargo's @@ -466,63 +462,38 @@ Adding dependencies Generally, the set of dependent crates is kept small. Think twice before adding a new external crate, especially if it comes with a large set of dependencies itself. Sometimes QEMU only needs a small subset of the -functionality; see for example QEMU's ``assertions`` module. +functionality; see for example QEMU's ``assertions`` module. Also, +choose a version of the crate that works with QEMU's minimum supported +Rust version (|msrv|). On top of this recommendation, adding external crates to QEMU is a slightly complicated process, mostly due to the need to teach Meson how -to build them. While Meson has initial support for parsing ``Cargo.lock`` -files, it is still highly experimental and is therefore not used. +to download them. While QEMU uses Meson's support for parsing ``Cargo.toml`` +files, it ships ``.wrap`` files instead of using ``Cargo.lock``; this way, +distros can adjust the set of dependencies to the exact versions they use. +The versions specified in QEMU's ``Cargo.lock`` must be the same as the +one in the wrap file. -Therefore, external crates must be added as subprojects for Meson to -learn how to build them, as well as to the relevant ``Cargo.toml`` files. -The versions specified in ``Cargo.lock`` must be the same as the -subprojects; note that the QEMU source tree forms a Cargo `workspace`__, -and therefore there is a single lock file for the whole build. - -__ https://doc.rust-lang.org/cargo/reference/workspaces.html#virtual-workspace - -Choose a version of the crate that works with QEMU's minimum supported -Rust version (|msrv|). - -Second, a new ``wrap`` file must be added to teach Meson how to download the -crate. The wrap file must be named ``NAME-SEMVER-rs.wrap``, where ``NAME`` +The wrap file must be named ``NAME-SEMVER-rs.wrap``, where ``NAME`` is the name of the crate and ``SEMVER`` is the version up to and including the first non-zero number. For example, a crate with version ``0.2.3`` will use ``0.2`` for its ``SEMVER``, while a crate with version ``1.0.84`` will use ``1``. -Third, the Meson rules to build the crate must be added at -``subprojects/NAME-SEMVER-rs/meson.build``. Generally this includes: +Usually, Meson is able to figure out how to build the crate, and also handles +cross compilation correctly. For crates that have a ``build.rs`` file, +equivalent rules must be added to +``subprojects/packagefiles/NAME-SEMVER-rs/meson/meson.build``. +The file can modify the ``extra_args`` and ``extra_deps`` variables, +which contain respectively the compiler arguments and external dependencies +for the crate. -* ``subproject`` and ``dependency`` lines for all dependent crates +After every change to the ``meson/meson.build`` file you have to update the +patched version with ``meson subprojects update --reset ``NAME-SEMVER-rs``. +This might be automated in the future. -* a ``static_library`` or ``rust.proc_macro`` line to perform the actual build - -* ``declare_dependency`` and a ``meson.override_dependency`` lines to expose - the result to QEMU and to other subprojects - -Remember to add ``native: true`` to ``dependency``, ``static_library`` and -``meson.override_dependency`` for dependencies of procedural macros. -If a crate is needed in both procedural macros and QEMU binaries, everything -apart from ``subproject`` must be duplicated to build both native and -non-native versions of the crate. - -It's important to specify the right compiler options. These include: - -* the language edition (which can be found in the ``Cargo.toml`` file) - -* the ``--cfg`` (which have to be "reverse engineered" from the ``build.rs`` - file of the crate). - -* usually, a ``--cap-lints allow`` argument to hide warnings from rustc - or clippy. - -After every change to the ``meson.build`` file you have to update the patched -version with ``meson subprojects update --reset ``NAME-SEMVER-rs``. This might -be automated in the future. - -Also, after every change to the ``meson.build`` file it is strongly suggested to -do a dummy change to the ``.wrap`` file (for example adding a comment like -``# version 2``), which will help Meson notice that the subproject is out of date. +Also, after every change to the file it is strongly suggested to do a dummy +change to the ``.wrap`` file (for example adding a comment like ``# version 2``), +which will help Meson notice that the subproject is out of date. As a last step, add the new subproject to ``scripts/archive-source.sh``, ``scripts/make-release`` and ``subprojects/.gitignore``. From 0836a79adafe9c5089839621ac761f84b92e95cf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 25 May 2026 17:53:42 +0200 Subject: [PATCH 646/860] scripts: remove now dead parts of rustc_args.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The logic to parse the [lints] section has been integrated into Meson and can be removed. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- meson.build | 1 - scripts/rust/rustc_args.py | 80 +------------------------------------- 2 files changed, 1 insertion(+), 80 deletions(-) diff --git a/meson.build b/meson.build index 3c6a82ab44..6dcfdceb50 100644 --- a/meson.build +++ b/meson.build @@ -130,7 +130,6 @@ endif if have_rust rustc_args = [find_program('scripts/rust/rustc_args.py'), - '--rustc-version', rustc.version(), '--workspace', meson.project_source_root()] rustfmt = find_program('rustfmt', required: false) diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 8098053720..107b944c16 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -25,10 +25,9 @@ along with this program. If not, see . """ import argparse -from dataclasses import dataclass import logging from pathlib import Path -from typing import Any, Iterable, List, Mapping, Optional, Set +from typing import Any, Iterable, Mapping, Optional, Set try: import tomllib @@ -74,40 +73,6 @@ class CargoTOML: return table -@dataclass -class LintFlag: - flags: List[str] - priority: int - - -def generate_lint_flags(cargo_toml: CargoTOML) -> Iterable[str]: - """Converts Cargo.toml lints to rustc -A/-D/-F/-W flags.""" - - toml_lints = cargo_toml.lints - - lint_list = [] - for k, v in toml_lints.items(): - prefix = "" if k == "rust" else k + "::" - for lint, data in v.items(): - level = data if isinstance(data, str) else data["level"] - priority = 0 if isinstance(data, str) else data.get("priority", 0) - if level == "deny": - flag = "-D" - elif level == "allow": - flag = "-A" - elif level == "warn": - flag = "-W" - elif level == "forbid": - flag = "-F" - else: - raise Exception(f"invalid level {level} for {prefix}{lint}") - lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) - - lint_list.sort(key=lambda x: x.priority) - for lint in lint_list: - yield from lint.flags - - def generate_cfg_flags(header: str, cargo_toml: CargoTOML) -> Iterable[str]: """Converts defines from config[..].h headers to rustc --cfg flags.""" @@ -154,60 +119,17 @@ def main() -> None: required=False, default=None, ) - parser.add_argument( - "--features", - action="store_true", - dest="features", - help="generate --check-cfg arguments for features", - required=False, - default=None, - ) - parser.add_argument( - "--lints", - action="store_true", - dest="lints", - help="generate arguments from [lints] table", - required=False, - default=None, - ) - parser.add_argument( - "--rustc-version", - metavar="VERSION", - dest="rustc_version", - action="store", - help="version of rustc", - required=False, - default="1.0.0", - ) args = parser.parse_args() if args.verbose: logging.basicConfig(level=logging.DEBUG) logging.debug("args: %s", args) - rustc_version = tuple((int(x) for x in args.rustc_version.split('.')[0:2])) if args.workspace: workspace_cargo_toml = Path(args.workspace, "Cargo.toml").resolve() cargo_toml = CargoTOML(args.cargo_toml, str(workspace_cargo_toml)) else: cargo_toml = CargoTOML(args.cargo_toml, None) - if args.lints: - for tok in generate_lint_flags(cargo_toml): - print(tok) - - if rustc_version >= (1, 80): - if args.lints: - print("--check-cfg") - print("cfg(test)") - for cfg in sorted(cargo_toml.check_cfg): - print("--check-cfg") - print(cfg) - if args.features: - for feature in cargo_toml.get_table("features"): - if feature != "default": - print("--check-cfg") - print(f'cfg(feature,values("{feature}"))') - for header in args.config_headers: for tok in generate_cfg_flags(header, cargo_toml): print(tok) From 7d33e694afdd9afe748aeea69bc768a26784e6ab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Jul 2026 12:01:24 +0200 Subject: [PATCH 647/860] rust: update Cargo dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update dependencies that are not used by meson, only by Cargo. Several packages have now reached v1 and therefore updates may be less intrusive in the future. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- Cargo.lock | 85 +++++++++++++++++++++++------------------------------- 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c94c2386d9..66f684098d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown", @@ -212,12 +212,6 @@ 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" @@ -342,29 +336,20 @@ 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" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.226" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -373,18 +358,18 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "syn" @@ -413,9 +398,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "7.0.5" +version = "7.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" dependencies = [ "cfg-expr", "heck", @@ -458,38 +443,43 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" dependencies = [ - "serde", + "indexmap", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ - "serde", + "serde_core", ] [[package]] -name = "toml_edit" -version = "0.22.27" +name = "toml_parser" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", "winnow", ] +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + [[package]] name = "trace" version = "0.1.0" @@ -537,9 +527,6 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "winnow" -version = "0.7.13" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" From 64574f1c517bc5a6eee56d94d7db46fd602044d8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 May 2025 10:23:44 +0200 Subject: [PATCH 648/860] rust: pl011: switch from bilge to bitfield-struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bilge crate is heavily reliant on traits and, because trait functions are never const, bilge and const mix about as well as water and oil. In addition, it has support for the zerocopy crate that only works for an older version, and is hard to update because the implementation doesn't like that zerocopy::FromBits and bilge::FromBits are the same name. zerocopy is definitely something that QEMU could use in the future. The bitfield-struct crate, instead, is built from the ground up to support const. Its use is pretty much the same (device code does not change at all, only register declarations do), with some things being more verbose and others being simpler. The code for the crate itself is much smaller, too. It does have two disadvantages: it does not let you annotate enums as bitfields, and it does not integrate with arbitrary-int. Thus, it requires manual size annotations for anything that is not a bool, iNN or uNN. Lack of support for arbitrary-int is a very small deal, while enums are a bit more annoying because they require some repetition and an implementation of two functions from_bits() and into_bits(). However, the latter is already provided by the "bits!" and "#[derive(common::TryInto)]" utilities, and thus is not manual in QEMU's case. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- Cargo.lock | 14 ++- Cargo.toml | 1 + rust/hw/char/pl011/Cargo.toml | 3 +- rust/hw/char/pl011/src/registers.rs | 108 +++++++++++------------ scripts/archive-source.sh | 1 + scripts/make-release | 1 + subprojects/.gitignore | 1 + subprojects/bitfield-struct-0.13-rs.wrap | 6 ++ 8 files changed, 75 insertions(+), 60 deletions(-) create mode 100644 subprojects/bitfield-struct-0.13-rs.wrap diff --git a/Cargo.lock b/Cargo.lock index 66f684098d..6f683e8bb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,17 @@ dependencies = [ "syn", ] +[[package]] +name = "bitfield-struct" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ca6739863c590881f038d033a146c51ddae239186a4327014839fd864f44ed5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bilge" version = "0.2.0" @@ -243,8 +254,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" name = "pl011" version = "0.1.0" dependencies = [ - "bilge", - "bilge-impl", + "bitfield-struct", "bits", "bql", "chardev", diff --git a/Cargo.toml b/Cargo.toml index 0566850ece..b35704faab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,5 +101,6 @@ used_underscore_binding = "deny" #wildcard_imports = "deny" # still have many bindings::* imports # these may have false positives +enum_variant_names = "allow" #option_if_let_else = "deny" cognitive_complexity = "deny" diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 5b319455ee..d303c241e4 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -14,8 +14,7 @@ rust-version.workspace = true [dependencies] glib-sys.workspace = true -bilge = { version = "0.2.0" } -bilge-impl = { version = "0.2.0" } +bitfield-struct = { version = "0.13" } bits = { path = "../../../bits" } common = { path = "../../../common" } util = { path = "../../../util" } diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index fa572811b2..a8ab8ad93d 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -5,12 +5,16 @@ //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. +// rustc prefers "constant-like" enums to use upper case names, but that +// is inconsistent in its own way. +#![allow(non_upper_case_globals)] + // For more detail see the PL011 Technical Reference Manual DDI0183: // https://developer.arm.com/documentation/ddi0183/latest/ -use bilge::prelude::*; +use bitfield_struct::bitfield; use bits::bits; -use migration::{impl_vmstate_bitsized, impl_vmstate_forward}; +use migration::impl_vmstate_forward; /// Offset of each register from the base memory address of the device. #[doc(alias = "offset")] @@ -78,14 +82,18 @@ pub enum RegisterOffset { /// The `UARTRSR` register is updated only when a read occurs /// from the `UARTDR` register with the same status information /// that can also be obtained by reading the `UARTDR` register -#[bitsize(8)] -#[derive(Clone, Copy, Default, DebugBits, FromBits)] +#[bitfield(u8)] pub struct Errors { pub framing_error: bool, pub parity_error: bool, pub break_error: bool, pub overrun_error: bool, - _reserved_unpredictable: u4, + #[bits(4)] + _reserved_unpredictable: u8, +} + +impl Errors { + pub const BREAK: Self = Errors::new().with_break_error(true); } /// Data Register, `UARTDR` @@ -93,19 +101,18 @@ pub struct Errors { /// The `UARTDR` register is the data register; write for TX and /// read for RX. It is a 12-bit register, where bits 7..0 are the /// character and bits 11..8 are error bits. -#[bitsize(32)] -#[derive(Clone, Copy, Default, DebugBits, FromBits)] +#[bitfield(u32)] #[doc(alias = "UARTDR")] pub struct Data { pub data: u8, + #[bits(8)] pub errors: Errors, _reserved: u16, } -impl_vmstate_bitsized!(Data); +impl_vmstate_forward!(Data); impl Data { - // bilge is not very const-friendly, unfortunately - pub const BREAK: Self = Self { value: 1 << 10 }; + pub const BREAK: Self = Self::new().with_errors(Errors::BREAK); } /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` @@ -119,13 +126,14 @@ impl Data { /// and UARTECR for writes, but really it's a single error status /// register where writing anything to the register clears the error /// bits. -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] +#[bitfield(u32)] pub struct ReceiveStatusErrorClear { + #[bits(8)] pub errors: Errors, - _reserved_unpredictable: u24, + #[bits(24)] + _reserved_unpredictable: u32, } -impl_vmstate_bitsized!(ReceiveStatusErrorClear); +impl_vmstate_forward!(ReceiveStatusErrorClear); impl ReceiveStatusErrorClear { pub fn set_from_data(&mut self, data: Data) { @@ -138,14 +146,7 @@ impl ReceiveStatusErrorClear { } } -impl Default for ReceiveStatusErrorClear { - fn default() -> Self { - 0.into() - } -} - -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] +#[bitfield(u32, default = false)] /// Flag Register, `UARTFR` /// /// This has the usual inbound RS232 modem-control signals, plus flags @@ -171,9 +172,10 @@ pub struct Flags { pub transmit_fifo_empty: bool, /// RI: Ring indicator pub ring_indicator: bool, - _reserved_zero_no_modify: u23, + #[bits(23)] + _reserved_zero_no_modify: u32, } -impl_vmstate_bitsized!(Flags); +impl_vmstate_forward!(Flags); impl Flags { pub fn reset(&mut self) { @@ -183,16 +185,14 @@ impl Flags { impl Default for Flags { fn default() -> Self { - let mut ret: Self = 0.into(); // After reset TXFF, RXFF, and BUSY are 0, and TXFE and RXFE are 1 - ret.set_receive_fifo_empty(true); - ret.set_transmit_fifo_empty(true); - ret + Self::from(0) + .with_receive_fifo_empty(true) + .with_transmit_fifo_empty(true) } } -#[bitsize(32)] -#[derive(Clone, Copy, DebugBits, FromBits)] +#[bitfield(u32)] /// Line Control Register, `UARTLCR_H` #[doc(alias = "UARTLCR_H")] pub struct LineControl { @@ -201,48 +201,46 @@ pub struct LineControl { /// PEN: Parity enable pub parity_enabled: bool, /// EPS: Even parity select + #[bits(1)] pub parity: Parity, /// STP2: Two stop bits select pub two_stops_bits: bool, /// FEN: Enable FIFOs + #[bits(1)] pub fifos_enabled: Mode, /// WLEN: Word length in bits /// b11 = 8 bits /// b10 = 7 bits /// b01 = 6 bits /// b00 = 5 bits. + #[bits(2)] pub word_length: WordLength, /// SPS Stick parity select pub sticky_parity: bool, /// 31:8 - Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u24, + #[bits(24)] + _reserved_zero_no_modify: u32, } -impl_vmstate_bitsized!(LineControl); +impl_vmstate_forward!(LineControl); impl LineControl { pub fn reset(&mut self) { // All the bits are cleared to 0 when reset. - *self = 0.into(); + *self = Self::default(); } } -impl Default for LineControl { - fn default() -> Self { - 0.into() - } -} - -#[bitsize(1)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] /// `EPS` "Even parity select", field of [Line Control /// register](LineControl). +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)] pub enum Parity { Odd = 0, Even = 1, } -#[bitsize(1)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)] /// `FEN` "Enable FIFOs" or Device mode, field of [Line Control /// register](LineControl). pub enum Mode { @@ -253,8 +251,8 @@ pub enum Mode { FIFO = 1, } -#[bitsize(2)] -#[derive(Clone, Copy, Debug, Eq, FromBits, PartialEq)] +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, common::TryInto)] #[allow(clippy::enum_variant_names)] /// `WLEN` Word length, field of [Line Control register](LineControl). /// @@ -276,9 +274,8 @@ pub enum WordLength { /// The `UARTCR` register is the control register. It contains various /// enable bits, and the bits to write to set the usual outbound RS232 /// modem control signals. All bits reset to 0 except TXE and RXE. -#[bitsize(32)] +#[bitfield(u32, default = false)] #[doc(alias = "UARTCR")] -#[derive(Clone, Copy, DebugBits, FromBits)] pub struct Control { /// `UARTEN` UART enable: 0 = UART is disabled. pub enable_uart: bool, @@ -286,9 +283,10 @@ pub struct Control { /// QEMU does not model this. pub enable_sir: bool, /// `SIRLP` SIR low-power IrDA mode. QEMU does not model this. - pub sir_lowpower_irda_mode: u1, + pub sir_lowpower_irda_mode: bool, /// Reserved, do not modify, read as zero. - _reserved_zero_no_modify: u4, + #[bits(4)] + _reserved_zero_no_modify: u8, /// `LBE` Loopback enable: feed UART output back to the input pub enable_loopback: bool, /// `TXE` Transmit enable @@ -310,21 +308,19 @@ pub struct Control { /// 31:16 - Reserved, do not modify, read as zero. _reserved_zero_no_modify2: u16, } -impl_vmstate_bitsized!(Control); +impl_vmstate_forward!(Control); impl Control { pub fn reset(&mut self) { - *self = 0.into(); - self.set_enable_receive(true); - self.set_enable_transmit(true); + *self = Self::default(); } } impl Default for Control { fn default() -> Self { - let mut ret: Self = 0.into(); - ret.reset(); - ret + Self::from(0) + .with_enable_receive(true) + .with_enable_transmit(true) } } diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index a37acab524..5d52644df9 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -34,6 +34,7 @@ subprojects=( berkeley-testfloat-3 bilge-0.2-rs bilge-impl-0.2-rs + bitfield-0.9-rs either-1-rs foreign-0.3-rs glib-sys-0.21-rs diff --git a/scripts/make-release b/scripts/make-release index 5f54b0e793..fc0dc4bceb 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -42,6 +42,7 @@ fi SUBPROJECTS="libvfio-user keycodemapdb 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 + bilge-impl-0.2-rs bitfield-0.9-rs either-1-rs foreign-0.3-rs itertools-0.11-rs libc-0.2-rs probe-0.5-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" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 011ce4dc3b..984f5865dc 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -11,6 +11,7 @@ /attrs-* /bilge-* /bilge-impl-* +/bitfield-struct-* /either-* /foreign-* /glib-sys-* diff --git a/subprojects/bitfield-struct-0.13-rs.wrap b/subprojects/bitfield-struct-0.13-rs.wrap new file mode 100644 index 0000000000..dcffe0abd8 --- /dev/null +++ b/subprojects/bitfield-struct-0.13-rs.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = bitfield-struct-0.13.0 +source_url = https://crates.io/api/v1/crates/bitfield-struct/0.13.0/download +source_filename = bitfield-struct-0.13.0.tar.gz +source_hash = 3ca6739863c590881f038d033a146c51ddae239186a4327014839fd864f44ed5 +method = cargo From 3ac34c6d25d84353c882ab0e11ea25229d4f1a48 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 24 Jul 2026 11:52:41 +0200 Subject: [PATCH 649/860] rust: remove bilge crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has just been replaced with bitfield-struct. Reviewed-by: Marc-André Lureau Signed-off-by: Paolo Bonzini --- Cargo.lock | 73 ------------------- rust/migration/src/vmstate.rs | 45 +----------- scripts/archive-source.sh | 6 -- scripts/make-release | 5 +- subprojects/.gitignore | 7 -- subprojects/arbitrary-int-1-rs.wrap | 9 --- subprojects/bilge-0.2-rs.wrap | 9 --- subprojects/bilge-impl-0.2-rs.wrap | 9 --- subprojects/either-1-rs.wrap | 9 --- subprojects/itertools-0.11-rs.wrap | 9 --- .../proc-macro-error-1-rs/meson/meson.build | 3 - subprojects/proc-macro-error-1-rs.wrap | 10 --- subprojects/proc-macro-error-attr-1-rs.wrap | 9 --- 13 files changed, 5 insertions(+), 198 deletions(-) delete mode 100644 subprojects/arbitrary-int-1-rs.wrap delete mode 100644 subprojects/bilge-0.2-rs.wrap delete mode 100644 subprojects/bilge-impl-0.2-rs.wrap delete mode 100644 subprojects/either-1-rs.wrap delete mode 100644 subprojects/itertools-0.11-rs.wrap delete mode 100644 subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build delete mode 100644 subprojects/proc-macro-error-1-rs.wrap delete mode 100644 subprojects/proc-macro-error-attr-1-rs.wrap diff --git a/Cargo.lock b/Cargo.lock index 6f683e8bb1..ad664a812d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,12 +8,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "arbitrary-int" -version = "1.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d" - [[package]] name = "attrs" version = "0.2.9" @@ -35,29 +29,6 @@ dependencies = [ "syn", ] -[[package]] -name = "bilge" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57" -dependencies = [ - "arbitrary-int", - "bilge-impl", -] - -[[package]] -name = "bilge-impl" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8" -dependencies = [ - "itertools", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "bits" version = "0.1.0" @@ -114,12 +85,6 @@ dependencies = [ "qemu_macros", ] -[[package]] -name = "either" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" - [[package]] name = "equivalent" version = "1.0.2" @@ -208,15 +173,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "libc" version = "0.2.162" @@ -274,29 +230,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136558b6e1ebaecc92755d0ffaf9421f519531bed30cc2ad23b22cb00965cc5e" -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro2" version = "1.0.95" @@ -529,12 +462,6 @@ 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 = "1.0.4" diff --git a/rust/migration/src/vmstate.rs b/rust/migration/src/vmstate.rs index 63d78b4f27..7409b592a2 100644 --- a/rust/migration/src/vmstate.rs +++ b/rust/migration/src/vmstate.rs @@ -11,8 +11,7 @@ //! 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), -//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and +//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) 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) @@ -90,7 +89,7 @@ macro_rules! call_func_with_field { /// The contents of this trait go straight into structs that are parsed by C /// code and used to introspect into other structs. Generally, you don't need /// to implement it except via macros that do it for you, such as -/// `impl_vmstate_bitsized!`. +/// `impl_vmstate_forward!`. pub unsafe trait VMState { /// The base contents of a `VMStateField` (minus the name and offset) for /// the type that is implementing the trait. @@ -133,9 +132,8 @@ 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_forward`](crate::impl_vmstate_forward), -/// [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), and -/// [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. +/// for them. The macros [`impl_vmstate_forward`](crate::impl_vmstate_forward) +/// and [`impl_vmstate_struct`](crate::impl_vmstate_struct) help with this. /// /// [`BqlCell`]: ../../bql/cell/struct.BqlCell.html /// [`BqlRefCell`]: ../../bql/cell/struct.BqlRefCell.html @@ -211,41 +209,6 @@ impl_vmstate_transparent!(std::pin::Pin where T: VMState); impl_vmstate_transparent!(common::Opaque where T: VMState); impl_vmstate_transparent!(std::mem::ManuallyDrop where T: VMState); -#[macro_export] -macro_rules! impl_vmstate_bitsized { - ($type:ty) => { - unsafe impl $crate::vmstate::VMState for $type { - const BASE: $crate::bindings::VMStateField = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - as ::bilge::prelude::Number>::UnderlyingType - as $crate::vmstate::VMState>::BASE; - const VARRAY_FLAG: $crate::bindings::VMStateFlags = - <<<$type as ::bilge::prelude::Bitsized>::ArbitraryInt - 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(()) - } - } - }; -} - // Scalar types using predefined VMStateInfos macro_rules! impl_vmstate_scalar { diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 5d52644df9..399b2d99e8 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -32,19 +32,13 @@ subprojects=( attrs-0.2-rs berkeley-softfloat-3 berkeley-testfloat-3 - bilge-0.2-rs - bilge-impl-0.2-rs bitfield-0.9-rs - either-1-rs foreign-0.3-rs glib-sys-0.21-rs - itertools-0.11-rs keycodemapdb libc-0.2-rs libvfio-user probe-0.5-rs - proc-macro-error-1-rs - proc-macro-error-attr-1-rs proc-macro2-1-rs quote-1-rs syn-2-rs diff --git a/scripts/make-release b/scripts/make-release index fc0dc4bceb..b5944d3928 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -41,10 +41,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 attrs-0.2-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs - bilge-impl-0.2-rs bitfield-0.9-rs either-1-rs foreign-0.3-rs itertools-0.11-rs - libc-0.2-rs probe-0.5-rs proc-macro2-1-rs - proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs + bitfield-0.9-rs foreign-0.3-rs libc-0.2-rs probe-0.5-rs proc-macro2-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" src="$1" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 984f5865dc..3559b2c758 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -7,19 +7,12 @@ /libvfio-user /slirp /anyhow-* -/arbitrary-int-* /attrs-* -/bilge-* -/bilge-impl-* /bitfield-struct-* -/either-* /foreign-* /glib-sys-* -/itertools-* /libc-* /probe-* -/proc-macro-error-* -/proc-macro-error-attr-* /proc-macro* /quote-* /syn-* diff --git a/subprojects/arbitrary-int-1-rs.wrap b/subprojects/arbitrary-int-1-rs.wrap deleted file mode 100644 index 5cf4b11e4a..0000000000 --- a/subprojects/arbitrary-int-1-rs.wrap +++ /dev/null @@ -1,9 +0,0 @@ -[wrap-file] -directory = arbitrary-int-1.2.7 -source_url = https://crates.io/api/v1/crates/arbitrary-int/1.2.7/download -source_filename = arbitrary-int-1.2.7.tar.gz -source_hash = c84fc003e338a6f69fbd4f7fe9f92b535ff13e9af8997f3b14b6ddff8b1df46d -method = cargo - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/bilge-0.2-rs.wrap b/subprojects/bilge-0.2-rs.wrap deleted file mode 100644 index cff10f6710..0000000000 --- a/subprojects/bilge-0.2-rs.wrap +++ /dev/null @@ -1,9 +0,0 @@ -[wrap-file] -directory = bilge-0.2.0 -source_url = https://crates.io/api/v1/crates/bilge/0.2.0/download -source_filename = bilge-0.2.0.tar.gz -source_hash = dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57 -method = cargo - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/bilge-impl-0.2-rs.wrap b/subprojects/bilge-impl-0.2-rs.wrap deleted file mode 100644 index f8963eba73..0000000000 --- a/subprojects/bilge-impl-0.2-rs.wrap +++ /dev/null @@ -1,9 +0,0 @@ -[wrap-file] -directory = bilge-impl-0.2.0 -source_url = https://crates.io/api/v1/crates/bilge-impl/0.2.0/download -source_filename = bilge-impl-0.2.0.tar.gz -source_hash = feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8 -method = cargo - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/either-1-rs.wrap b/subprojects/either-1-rs.wrap deleted file mode 100644 index 51c97df153..0000000000 --- a/subprojects/either-1-rs.wrap +++ /dev/null @@ -1,9 +0,0 @@ -[wrap-file] -directory = either-1.12.0 -source_url = https://crates.io/api/v1/crates/either/1.12.0/download -source_filename = either-1.12.0.tar.gz -source_hash = 3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b -method = cargo - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/itertools-0.11-rs.wrap b/subprojects/itertools-0.11-rs.wrap deleted file mode 100644 index e123ab389a..0000000000 --- a/subprojects/itertools-0.11-rs.wrap +++ /dev/null @@ -1,9 +0,0 @@ -[wrap-file] -directory = itertools-0.11.0 -source_url = https://crates.io/api/v1/crates/itertools/0.11.0/download -source_filename = itertools-0.11.0.tar.gz -source_hash = b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57 -method = cargo - -# bump this version number on every change to meson.build or the patches: -# v2 diff --git a/subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build b/subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build deleted file mode 100644 index 52a244d2b1..0000000000 --- a/subprojects/packagefiles/proc-macro-error-1-rs/meson/meson.build +++ /dev/null @@ -1,3 +0,0 @@ -extra_args = [ - '--cfg', 'use_fallback', -] diff --git a/subprojects/proc-macro-error-1-rs.wrap b/subprojects/proc-macro-error-1-rs.wrap deleted file mode 100644 index e57921bfad..0000000000 --- a/subprojects/proc-macro-error-1-rs.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -directory = proc-macro-error-1.0.4 -source_url = https://crates.io/api/v1/crates/proc-macro-error/1.0.4/download -source_filename = proc-macro-error-1.0.4.tar.gz -source_hash = da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c -method = cargo -patch_directory = proc-macro-error-1-rs - -# bump this version number on every change to meson.build or the patches: -# v3 diff --git a/subprojects/proc-macro-error-attr-1-rs.wrap b/subprojects/proc-macro-error-attr-1-rs.wrap deleted file mode 100644 index 456d2c2b6d..0000000000 --- a/subprojects/proc-macro-error-attr-1-rs.wrap +++ /dev/null @@ -1,9 +0,0 @@ -[wrap-file] -directory = proc-macro-error-attr-1.0.4 -source_url = https://crates.io/api/v1/crates/proc-macro-error-attr/1.0.4/download -source_filename = proc-macro-error-attr-1.0.4.tar.gz -source_hash = a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869 -method = cargo - -# bump this version number on every change to meson.build or the patches: -# v2 From dd45df326e420ed5fc3c181b716ff459beb4ac9e Mon Sep 17 00:00:00 2001 From: Song Gao Date: Thu, 13 Aug 2026 19:09:58 +0800 Subject: [PATCH 650/860] target/loongarch: Add kvm support dintc This patch adds Kvm put/get msgint CSRs and check msgint feature. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20260813111000.446232-2-gaosong@loongson.cn> --- target/loongarch/cpu.c | 5 ++- target/loongarch/cpu.h | 1 + target/loongarch/kvm/kvm.c | 74 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 8cd5bc18a3..48e1ad83c9 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -451,9 +451,6 @@ static void loongarch_max_initfn(Object *obj) cpu->ptw = ON_OFF_AUTO_AUTO; if (tcg_enabled()) { - cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); - cpu->msgint = ON_OFF_AUTO_AUTO; - uint32_t data = cpu->env.cpucfg[2]; data = FIELD_DP32(data, CPUCFG2, HPTW, 1); /* Enable LA v1.1 instructions */ @@ -468,6 +465,8 @@ static void loongarch_max_initfn(Object *obj) data = FIELD_DP32(data, CPUCFG3, DBAR_HINTS, 1); cpu->env.cpucfg[3] = data; } + cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); + cpu->msgint = ON_OFF_AUTO_AUTO; } #if defined(CONFIG_KVM) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index ad30c73167..97db3b453f 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -301,6 +301,7 @@ enum loongarch_features { LOONGARCH_FEATURE_PV_IPI, LOONGARCH_FEATURE_STEALTIME, LOONGARCH_FEATURE_PTW, + LOONGARCH_FEATURE_MSGINT, }; typedef struct LoongArchBT { diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 4d0cad5732..15082a01f8 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -36,6 +36,13 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { KVM_CAP_LAST_INFO }; +static bool kvm_cpu_has_msgint(CPUState *cs) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + + return FIELD_EX64(cpu->env.cpucfg[1], CPUCFG1, MSG_INT); +} + static int kvm_get_stealtime(CPUState *cs) { CPULoongArchState *env = cpu_env(cs); @@ -362,6 +369,25 @@ static int kvm_loongarch_get_csr(CPUState *cs) ret |= kvm_loongarch_get_pmu(cs); + /* + * CSR register MSGIS getting must be put after CSR register CSR_ESTAT, + * Since register CSR_ESTAT will sync software pending MSGINT status to + * hardware register and modify HW CSR MSGIS registers. + */ + if (kvm_cpu_has_msgint(cs)) { + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(0)), + &sys->CSR_MSGIS[0]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(1)), + &sys->CSR_MSGIS[1]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(2)), + &sys->CSR_MSGIS[2]); + + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(3)), + &sys->CSR_MSGIS[3]); + } + ret |= kvm_get_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_TVAL), &sys->CSR_TVAL); @@ -538,6 +564,20 @@ static int kvm_loongarch_put_csr(CPUState *cs, KvmPutState level) ret |= kvm_loongarch_put_pmu(cs); + if (kvm_cpu_has_msgint(cs)) { + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(0)), + &sys->CSR_MSGIS[0]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(1)), + &sys->CSR_MSGIS[1]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(2)), + &sys->CSR_MSGIS[2]); + + ret |= kvm_set_one_reg(cs, KVM_IOC_CSRID(LOONGARCH_CSR_MSGIS(3)), + &sys->CSR_MSGIS[3]); + } + /* * timer cfg must be put at last since it is used to enable * guest timer @@ -993,6 +1033,12 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); return (ret == 0); + case LOONGARCH_FEATURE_MSGINT: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_MSGINT; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + default: return false; } @@ -1166,6 +1212,28 @@ static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) return 0; } +static int kvm_cpu_check_msgint(CPUState *cs, Error **errp) +{ + CPULoongArchState *env = cpu_env(cs); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_MSGINT); + env->cpucfg[1] = FIELD_DP32(env->cpucfg[1], CPUCFG1, MSG_INT, 0); + if (cpu->msgint == ON_OFF_AUTO_ON) { + if (kvm_supported) { + env->cpucfg[1] = FIELD_DP32(env->cpucfg[1], CPUCFG1, MSG_INT, 1); + } else { + error_setg(errp, "'msgint' feature not supported by KVM on this host"); + return -ENOTSUP; + } + } else if ((cpu->msgint == ON_OFF_AUTO_AUTO) && kvm_supported) { + env->cpucfg[1] = FIELD_DP32(env->cpucfg[1], CPUCFG1, MSG_INT, 1); + } + + return 0; +} + int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) { return 0; @@ -1221,6 +1289,12 @@ int kvm_arch_init_vcpu(CPUState *cs) return ret; } + ret = kvm_cpu_check_msgint(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + return ret; + } + return 0; } From 06e75b3923a4dc265155a2859c33fc4405718771 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Thu, 13 Aug 2026 19:09:59 +0800 Subject: [PATCH 651/860] Add dintc kvm_irqchip_in_kernel support Function kvm_dintc_realize() is added if kvm_irqchip_in_kernel is set. It is to create and initialize DINTC device in kernel mode. and use kvm_irqchip_send_msi() to send msi to kernel. Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20260813111000.446232-3-gaosong@loongson.cn> --- hw/intc/loongarch_dintc.c | 13 +++++++++ hw/intc/loongarch_dintc_kvm.c | 48 +++++++++++++++++++++++++++++++ hw/intc/meson.build | 2 ++ include/hw/intc/loongarch_dintc.h | 10 +++++++ 4 files changed, 73 insertions(+) create mode 100644 hw/intc/loongarch_dintc_kvm.c diff --git a/hw/intc/loongarch_dintc.c b/hw/intc/loongarch_dintc.c index e40292887c..94d418261c 100644 --- a/hw/intc/loongarch_dintc.c +++ b/hw/intc/loongarch_dintc.c @@ -68,6 +68,15 @@ static void loongarch_dintc_mem_write(void *opaque, hwaddr addr, } irq_num = FIELD_EX64(msg_addr, MSG_ADDR, IRQ_NUM); + if (kvm_irqchip_in_kernel()) { + MSIMessage msg; + + msg.address = msg_addr; + msg.data = val; + kvm_irqchip_send_msi(kvm_state, msg); + return; + } + 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); @@ -110,6 +119,10 @@ static void loongarch_dintc_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, &s->cpu[i].parent_irq, 1); } + if (kvm_irqchip_in_kernel()) { + kvm_dintc_realize(dev, errp); + } + return; } diff --git a/hw/intc/loongarch_dintc_kvm.c b/hw/intc/loongarch_dintc_kvm.c new file mode 100644 index 0000000000..1137bcd9a6 --- /dev/null +++ b/hw/intc/loongarch_dintc_kvm.c @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch DINTC interrupt kvm support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/intc/loongarch_dintc.h" +#include "linux/kvm.h" +#include "qapi/error.h" +#include "system/kvm.h" + +void kvm_dintc_realize(DeviceState *dev, Error **errp) +{ + LoongArchDINTCState *lds = LOONGARCH_DINTC(dev); + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_DMSINTC, false); + if (ret < 0) { + fprintf(stderr, "create KVM_DEV_TYPE_LOONGARCH_AVEC failed: %s\n", + strerror(-ret)); + abort(); + } + lds->dev_fd = ret; + + /* init dintc config */ + lds->msg_addr_base = VIRT_DINTC_BASE; + lds->msg_addr_size = VIRT_DINTC_SIZE; + + ret = kvm_device_access(lds->dev_fd, KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL, + KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_BASE, + &lds->msg_addr_base, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_DEV_LOONGARCH_DINTC_MSG_ADDR_BASE failed: %s\n", + strerror(ret)); + abort(); + } + + ret = kvm_device_access(lds->dev_fd, KVM_DEV_LOONGARCH_DMSINTC_GRP_CTRL, + KVM_DEV_LOONGARCH_DMSINTC_MSG_ADDR_SIZE, + &lds->msg_addr_size, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_DEV_LOONGARCH_DINTC_MSG_ADDR_SIZE failed: %s\n", + strerror(ret)); + abort(); + } +} diff --git a/hw/intc/meson.build b/hw/intc/meson.build index a1401cb521..4373f16d0b 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -90,3 +90,5 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extio 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')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_DINTC'], + if_true: files('loongarch_dintc_kvm.c')) diff --git a/include/hw/intc/loongarch_dintc.h b/include/hw/intc/loongarch_dintc.h index 1f4f65705a..95a7879e26 100644 --- a/include/hw/intc/loongarch_dintc.h +++ b/include/hw/intc/loongarch_dintc.h @@ -9,8 +9,13 @@ #include "hw/core/sysbus.h" #include "hw/loongarch/virt.h" #include "system/memory.h" +#include "hw/pci-host/ls7a.h" #define NR_VECTORS 256 +#define IRQ_BIT_BASE 5 +#define IRQ_BIT_LEN 8 +#define CPU_BIT_BASE 13 +#define CPU_BIT_LEN 8 #define TYPE_LOONGARCH_DINTC "loongarch_dintc" OBJECT_DECLARE_TYPE(LoongArchDINTCState, LoongArchDINTCClass, LOONGARCH_DINTC) @@ -25,7 +30,10 @@ struct LoongArchDINTCState { SysBusDevice parent_obj; MemoryRegion dintc_mmio; DINTCCore *cpu; + int dev_fd; uint32_t num_cpu; + uint64_t msg_addr_base; + uint64_t msg_addr_size; }; struct LoongArchDINTCClass { @@ -34,3 +42,5 @@ struct LoongArchDINTCClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; }; + +void kvm_dintc_realize(DeviceState *dev, Error **errp); From 11563efaba79377d2004041da514f857c876a151 Mon Sep 17 00:00:00 2001 From: gaosong Date: Thu, 13 Aug 2026 19:10:00 +0800 Subject: [PATCH 652/860] target/loongarch: KVM disable msgint In KVM mode, msgint is disabled by default; please enable it manually. e.g ... -cpu max,msgint=on ... Signed-off-by: gaosong Reviewed-by: Bibo Mao Message-ID: <20260813111000.446232-4-gaosong@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 48e1ad83c9..bd819247c7 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -450,6 +450,9 @@ static void loongarch_max_initfn(Object *obj) loongarch_la464_initfn(obj); cpu->ptw = ON_OFF_AUTO_AUTO; + if (kvm_enabled()){ + cpu->msgint=ON_OFF_AUTO_OFF; + } if (tcg_enabled()) { uint32_t data = cpu->env.cpucfg[2]; data = FIELD_DP32(data, CPUCFG2, HPTW, 1); @@ -464,9 +467,9 @@ static void loongarch_max_initfn(Object *obj) data = cpu->env.cpucfg[3]; data = FIELD_DP32(data, CPUCFG3, DBAR_HINTS, 1); cpu->env.cpucfg[3] = data; + cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); + cpu->msgint = ON_OFF_AUTO_AUTO; } - cpu->env.cpucfg[1] = FIELD_DP32(cpu->env.cpucfg[1], CPUCFG1, MSG_INT, 1); - cpu->msgint = ON_OFF_AUTO_AUTO; } #if defined(CONFIG_KVM) From 215a4fc08f2aafce54c168cf41c64c79187430c1 Mon Sep 17 00:00:00 2001 From: Jan Mercl <0xjnml@gmail.com> Date: Wed, 19 Aug 2026 15:24:22 +0200 Subject: [PATCH 653/860] target/loongarch: check FPE before reading fcc in bceqz/bcnez gen_cz_bc() loads env->cf[cj] without CHECK_FPE, unlike every other translator that touches an fcc register (trans_fcmp.c.inc and trans_fmov.c.inc, for movcf2gr/movgr2cf/movcf2fr/movfr2cf/fsel). A guest that manages the FPU lazily -- Linux clears CSR.EUEN.FPE in lose_fpu() on every context switch -- relies on the next fcc access raising a Floating-Point-Disabled exception so the kernel can restore that task's fcc. Because bceqz and bcnez never raise it, they branch on the condition flag left behind by whichever task last owned the FPU. Real Loongson hardware does raise the exception, so this is TCG-only. It surfaces as Go binaries dying at startup in runtime.check() with "fatal error: float64nan1" -- roughly one process start in a thousand once the guest has more runnable tasks than vCPUs -- and in general as a conditional branch silently taking the wrong path. With four tasks each executing 5M bcnez on a 2-vCPU guest, master mispredicts 89 of 20000000. With this patch, 0 of 100000000 over five runs; a Loongson-3C5000 is likewise 0 of 600000000. CHECK_FPE is defined in trans_farith.c.inc, which translate.c includes before trans_branch.c.inc, so it is already in scope. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4209 Cc: qemu-stable@nongnu.org Signed-off-by: Jan Mercl <0xjnml@gmail.com> Reviewed-by: Richard Henderson Message-ID: <20260819132422.5164-1-0xjnml@gmail.com> Signed-off-by: Song Gao --- target/loongarch/tcg/insn_trans/trans_branch.c.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/loongarch/tcg/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc index f94c1f37ab..da07778658 100644 --- a/target/loongarch/tcg/insn_trans/trans_branch.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc @@ -66,6 +66,8 @@ static bool gen_cz_bc(DisasContext *ctx, arg_c_offs *a, TCGCond cond) TCGv src1 = tcg_temp_new(); TCGv src2 = tcg_constant_tl(0); + CHECK_FPE; + tcg_gen_ld8u_tl(src1, tcg_env, offsetof(CPULoongArchState, cf[a->cj])); gen_bc(ctx, src1, src2, a->offs, cond); From 184417d4adc97e10d07add5b4129387dadf1e131 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Tue, 18 Aug 2026 14:39:05 +0530 Subject: [PATCH 654/860] MAINTAINERS: elevating myself to be a maintainer for igvm I have worked enough on the igvm to be confident to elevate myself to a maintainer role. Signed-off-by: Ani Sinha Message-ID: <20260818090905.87090-1-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index b51f5c3e60..9e0a2a656f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4118,7 +4118,7 @@ F: tests/uefi-test-tools/ IGVM Firmware M: Gerd Hoffmann M: Stefano Garzarella -R: Ani Sinha +M: Ani Sinha S: Maintained F: backends/igvm*.c F: docs/system/igvm.rst From acce06adc0651fe5a19596ac5733142031343c60 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 17 Aug 2026 19:49:56 +0530 Subject: [PATCH 655/860] igvm: store IgvmCfg pointer in QIgvm Store a pointer to IgvmCfg instead of only IgvmFile in QIgvm. Allows to store additional state in the (persistent) IgvmCfg struct. Reviewed-by: Ani Sinha Reviewed-by: Alexander Graf Signed-off-by: Gerd Hoffmann Message-ID: <20260817142010.80693-2-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- backends/igvm.c | 57 ++++++++++++++++++++-------------- include/system/igvm-internal.h | 2 +- 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/backends/igvm.c b/backends/igvm.c index 80e87fe602..534032fed8 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -178,7 +178,8 @@ static int qigvm_handler(QIgvm *ctx, IgvmVariableHeaderType raw_type, if (handlers[handler].type != type) { continue; } - header_handle = igvm_get_header(ctx->file, handlers[handler].section, + header_handle = igvm_get_header(ctx->cfg->file, + handlers[handler].section, ctx->current_header_index); if (header_handle < 0) { error_setg( @@ -187,7 +188,7 @@ static int qigvm_handler(QIgvm *ctx, IgvmVariableHeaderType raw_type, (int)header_handle); return -1; } - header_data = igvm_get_buffer(ctx->file, header_handle); + header_data = igvm_get_buffer(ctx->cfg->file, header_handle); if (header_data != NULL) { header_data += sizeof(IGVM_VHS_VARIABLE_HEADER); result = handlers[handler].handler(ctx, header_data, errp); @@ -198,7 +199,7 @@ static int qigvm_handler(QIgvm *ctx, IgvmVariableHeaderType raw_type, header_handle, type); result = -1; } - igvm_free_buffer(ctx->file, header_handle); + igvm_free_buffer(ctx->cfg->file, header_handle); return result; } @@ -344,7 +345,8 @@ static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, for (page_index = 0; page_index < page_count; page_index++) { data_handle = igvm_get_header_data( - ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index); + ctx->cfg->file, IGVM_HEADER_SECTION_DIRECTIVE, + page_index + start_index); if (data_handle == IGVMAPI_NO_DATA) { /* No data indicates a zero page */ memset(®ion[page_index * page_size], 0, page_size); @@ -357,7 +359,7 @@ static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, return -1; } else { zero = false; - data_size = igvm_get_buffer_size(ctx->file, data_handle); + data_size = igvm_get_buffer_size(ctx->cfg->file, data_handle); if (data_size < page_size) { memset(®ion[page_index * page_size], 0, page_size); } else if (data_size > page_size) { @@ -367,14 +369,14 @@ static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, page_index + start_index); return -1; } - data = igvm_get_buffer(ctx->file, data_handle); + data = igvm_get_buffer(ctx->cfg->file, data_handle); if (data == NULL) { error_setg(errp, "IGVM: No buffer for handle %d", data_handle); - igvm_free_buffer(ctx->file, data_handle); + igvm_free_buffer(ctx->cfg->file, data_handle); return -1; } memcpy(®ion[page_index * page_size], data, data_size); - igvm_free_buffer(ctx->file, data_handle); + igvm_free_buffer(ctx->cfg->file, data_handle); } } @@ -411,7 +413,8 @@ static int qigvm_process_mem_page(QIgvm *ctx, ctx->region_start = page_data->gpa; ctx->region_start_index = ctx->current_header_index; } else { - if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_index, + if (!qigvm_page_attrs_equal(ctx->cfg->file, + ctx->current_header_index, page_data, &ctx->region_prev_page_data) || ((ctx->region_prev_page_data.gpa + @@ -474,7 +477,8 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, return 0; } - data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, + data_handle = igvm_get_header_data(ctx->cfg->file, + IGVM_HEADER_SECTION_DIRECTIVE, ctx->current_header_index); if (data_handle < 0) { error_setg(errp, "Invalid VP context in IGVM file. Error code: %X", @@ -482,7 +486,7 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, return -1; } - data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle); + data = (uint8_t *)igvm_get_buffer(ctx->cfg->file, data_handle); if (data == NULL) { error_setg(errp, "IGVM: No buffer for handle %d", data_handle); result = -1; @@ -491,7 +495,8 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, if (ctx->machine_state->cgs) { result = ctx->cgsc->set_guest_state( - vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle), + vp_context->gpa, data, + igvm_get_buffer_size(ctx->cfg->file, data_handle), CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp); } else if (target_arch() == SYS_EMU_TARGET_X86_64) { result = qigvm_x86_set_vp_context(data, vp_context->vp_index, errp); @@ -504,7 +509,7 @@ static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, } exit: - igvm_free_buffer(ctx->file, data_handle); + igvm_free_buffer(ctx->cfg->file, data_handle); if (result < 0) { return result; } @@ -863,7 +868,8 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) uint32_t compatibility_mask_sev_snp = 0; uint32_t compatibility_mask = 0; - header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM); + header_count = igvm_header_count(ctx->cfg->file, + IGVM_HEADER_SECTION_PLATFORM); if (header_count < 0) { error_setg(errp, "Invalid platform header count in IGVM file. Error code: %X", @@ -874,11 +880,11 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) for (header_index = 0; header_index < (unsigned)header_count; header_index++) { IgvmVariableHeaderType typ = igvm_get_header_type( - ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + ctx->cfg->file, IGVM_HEADER_SECTION_PLATFORM, header_index); typ = igvm_vht_type(typ); if (typ == IGVM_VHT_SUPPORTED_PLATFORM) { header_handle = igvm_get_header( - ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + ctx->cfg->file, IGVM_HEADER_SECTION_PLATFORM, header_index); if (header_handle < 0) { error_setg(errp, "Invalid platform header in IGVM file. " @@ -887,11 +893,11 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) return -1; } platform = - (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file, + (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->cfg->file, header_handle)); if (platform == NULL) { error_setg(errp, "IGVM: No buffer for handle %d", header_handle); - igvm_free_buffer(ctx->file, header_handle); + igvm_free_buffer(ctx->cfg->file, header_handle); return -1; } @@ -922,7 +928,7 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) } else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) { compatibility_mask = platform->compatibility_mask; } - igvm_free_buffer(ctx->file, header_handle); + igvm_free_buffer(ctx->cfg->file, header_handle); } } /* Choose the strongest supported isolation technology */ @@ -999,7 +1005,7 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, error_setg(errp, "No IGVM file loaded."); return -1; } - ctx.file = cfg->file; + ctx.cfg = cfg; trace_igvm_process_file(cfg->file, onlyVpContext); ctx.machine_state = machine_state; @@ -1021,7 +1027,8 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, goto cleanup; } - header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE); + header_count = igvm_header_count(ctx.cfg->file, + IGVM_HEADER_SECTION_DIRECTIVE); if (header_count <= 0) { error_setg( errp, "Invalid directive header count in IGVM file. Error code: %X", @@ -1035,7 +1042,8 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, ctx.current_header_index < (unsigned)header_count; ctx.current_header_index++) { IgvmVariableHeaderType raw_type = igvm_get_header_type( - ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index); + ctx.cfg->file, IGVM_HEADER_SECTION_DIRECTIVE, + ctx.current_header_index); if (!onlyVpContext || igvm_vht_type(raw_type) == IGVM_VHT_VP_CONTEXT) { if (qigvm_handler(&ctx, raw_type, errp) < 0) { goto cleanup_parameters; @@ -1053,7 +1061,7 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, } header_count = - igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION); + igvm_header_count(ctx.cfg->file, IGVM_HEADER_SECTION_INITIALIZATION); if (header_count < 0) { error_setg( errp, @@ -1066,7 +1074,8 @@ int qigvm_process_file(IgvmCfg *cfg, MachineState *machine_state, ctx.current_header_index < (unsigned)header_count; ctx.current_header_index++) { IgvmVariableHeaderType type = - igvm_get_header_type(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION, + igvm_get_header_type(ctx.cfg->file, + IGVM_HEADER_SECTION_INITIALIZATION, ctx.current_header_index); if (qigvm_handler(&ctx, type, errp) < 0) { goto cleanup_parameters; diff --git a/include/system/igvm-internal.h b/include/system/igvm-internal.h index 7eb3792ed8..b5720fae0c 100644 --- a/include/system/igvm-internal.h +++ b/include/system/igvm-internal.h @@ -43,7 +43,7 @@ typedef struct QIgvmParameterData { * file. */ struct QIgvm { - IgvmHandle file; + IgvmCfg *cfg; MachineState *machine_state; ConfidentialGuestSupportClass *cgsc; uint32_t compatibility_mask; From 14eb43761dc2d732f8f86b051f31323f1cf1fd80 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 17 Aug 2026 19:49:57 +0530 Subject: [PATCH 656/860] igvm: track memory regions Memory regions added by the current IGVM needs to be tracked so that they can be freed when a new IGVM is loaded. Reviewed-by: Ani Sinha Reviewed-by: Alexander Graf Signed-off-by: Gerd Hoffmann Message-ID: <20260817142010.80693-3-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- backends/igvm-cfg.c | 1 + backends/igvm.c | 20 ++++++++++++-------- include/system/igvm-internal.h | 6 ++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c index e1f09855f6..38438a7b1e 100644 --- a/backends/igvm-cfg.c +++ b/backends/igvm-cfg.c @@ -65,6 +65,7 @@ static void igvm_complete(UserCreatable *uc, Error **errp) IgvmCfg *igvm = IGVM_CFG(uc); igvm->file = qigvm_file_init(igvm->filename, errp); + QTAILQ_INIT(&igvm->memory_regions); } OBJECT_DEFINE_TYPE_WITH_INTERFACES(IgvmCfg, igvm_cfg, IGVM_CFG, OBJECT, diff --git a/backends/igvm.c b/backends/igvm.c index 534032fed8..9e7c90d386 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -220,7 +220,7 @@ static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size, int region_identifier, Error **errp) { ERRP_GUARD(); - MemoryRegion *igvm_pages = NULL; + IgvmMemoryRegion *imr = NULL; Int128 gpa_region_size; MemoryRegionSection mrs = memory_region_find(get_system_memory(), addr, size); @@ -254,23 +254,27 @@ static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size, */ g_autofree char *region_name = g_strdup_printf("igvm.%X", region_identifier); - igvm_pages = g_new0(MemoryRegion, 1); + imr = g_new0(IgvmMemoryRegion, 1); + imr->mr = g_new0(MemoryRegion, 1); if (ctx->machine_state->cgs && ctx->machine_state->cgs->require_guest_memfd) { - if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL, + if (!memory_region_init_ram_guest_memfd(imr->mr, NULL, region_name, size, errp)) { - g_free(igvm_pages); + g_free(imr->mr); + g_free(imr); return NULL; } } else { - if (!memory_region_init_ram(igvm_pages, NULL, region_name, size, + if (!memory_region_init_ram(imr->mr, NULL, region_name, size, errp)) { - g_free(igvm_pages); + g_free(imr->mr); + g_free(imr); return NULL; } } - memory_region_add_subregion(get_system_memory(), addr, igvm_pages); - return memory_region_get_ram_ptr(igvm_pages); + memory_region_add_subregion(get_system_memory(), addr, imr->mr); + QTAILQ_INSERT_TAIL(&ctx->cfg->memory_regions, imr, next); + return memory_region_get_ram_ptr(imr->mr); } } diff --git a/include/system/igvm-internal.h b/include/system/igvm-internal.h index b5720fae0c..9e9fa1d9af 100644 --- a/include/system/igvm-internal.h +++ b/include/system/igvm-internal.h @@ -18,6 +18,11 @@ #include "system/confidential-guest-support.h" #include +typedef struct IgvmMemoryRegion { + QTAILQ_ENTRY(IgvmMemoryRegion) next; + MemoryRegion *mr; +} IgvmMemoryRegion; + struct IgvmCfg { Object parent_obj; @@ -29,6 +34,7 @@ struct IgvmCfg { char *filename; IgvmHandle file; ResettableState reset_state; + QTAILQ_HEAD(, IgvmMemoryRegion) memory_regions; }; typedef struct QIgvmParameterData { From 94cfb5afe77858536265c957f47d10d85d75908c Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 17 Aug 2026 19:49:58 +0530 Subject: [PATCH 657/860] igvm: cleanup memory regions This change cleans up existing memory regions that were created by the current IGVM. The cleanup would be necessary when a new IGVM is loaded. This cleanup function is called in a subsequent patch. Reviewed-by: Alexander Graf Tested-by: Ani Sinha Signed-off-by: Gerd Hoffmann Message-ID: <20260817142010.80693-4-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- backends/igvm-cfg.c | 2 ++ backends/igvm.c | 19 +++++++++++++++++++ include/system/igvm.h | 1 + 3 files changed, 22 insertions(+) diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c index 38438a7b1e..935ba54f54 100644 --- a/backends/igvm-cfg.c +++ b/backends/igvm-cfg.c @@ -52,6 +52,8 @@ static void igvm_reset_hold(Object *obj, ResetType type) trace_igvm_reset_hold(type); + /* cleanup existing memory regions first */ + qigvm_cleanup_memory(igvm); qigvm_process_file(igvm, ms, false, &error_fatal); } diff --git a/backends/igvm.c b/backends/igvm.c index 9e7c90d386..9b101c8182 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -14,6 +14,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/target-info-qapi.h" +#include "migration/vmstate.h" #include "system/igvm.h" #include "system/igvm-cfg.h" #include "system/igvm-internal.h" @@ -1109,3 +1110,21 @@ cleanup_parameters: cleanup: return retval; } + +/* + * cleanup any memory regions created by qigvm_prepare_memory() + */ +void qigvm_cleanup_memory(IgvmCfg *cfg) +{ + IgvmMemoryRegion *imr, *tmp; + + QTAILQ_FOREACH_SAFE(imr, &cfg->memory_regions, next, tmp) + { + memory_region_del_subregion(get_system_memory(), imr->mr); + vmstate_unregister_ram(imr->mr, NULL); + QTAILQ_REMOVE(&cfg->memory_regions, imr, next); + /* this triggers MemoryRegion cleanup */ + object_unparent(OBJECT(imr->mr)); + g_free(imr); + } +} diff --git a/include/system/igvm.h b/include/system/igvm.h index 64d3542311..e219f1a4ed 100644 --- a/include/system/igvm.h +++ b/include/system/igvm.h @@ -20,6 +20,7 @@ typedef struct QIgvm QIgvm; int qigvm_process_file(IgvmCfg *igvm, MachineState *machine_state, bool onlyVpContext, Error **errp); +void qigvm_cleanup_memory(IgvmCfg *igvm); /* x86 native */ int qigvm_x86_get_mem_map_entry(int index, From 0102503dc58d5f34e49f4d9cb7020271aa90b149 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 17 Aug 2026 19:49:59 +0530 Subject: [PATCH 658/860] system/memory: add a tracepoint for memory_region_finalize cosmetic: add a tracepoint to track when memory regions are getting freed. Useful for debugging and tracking all freed memory regions. Reviewed-by: Peter Xu Signed-off-by: Ani Sinha Message-ID: <20260817142010.80693-5-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- system/memory.c | 1 + system/trace-events | 1 + 2 files changed, 2 insertions(+) diff --git a/system/memory.c b/system/memory.c index da710bbade..9760721e45 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1691,6 +1691,7 @@ static void memory_region_finalize(Object *obj) { MemoryRegion *mr = MEMORY_REGION(obj); + trace_memory_region_finalize(mr->name); /* * 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 diff --git a/system/trace-events b/system/trace-events index d483b31419..38ad0be8a9 100644 --- a/system/trace-events +++ b/system/trace-events @@ -25,6 +25,7 @@ flatview_new(void *view, void *root) "%p (root %p)" flatview_destroy(void *view, void *root) "%p (root %p)" flatview_destroy_rcu(void *view, void *root) "%p (root %p)" global_dirty_changed(unsigned int bitmask) "bitmask 0x%"PRIx32 +memory_region_finalize(const char* name) "mr %s" # physmem.c address_space_map(void *as, uint64_t addr, uint64_t len, bool is_write, uint32_t attrs) "as:%p addr 0x%"PRIx64":%"PRIx64" write:%d attrs:0x%x" From 68e54556e731432dd5ceff3daf5889e2096d42ae Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 17 Aug 2026 19:50:00 +0530 Subject: [PATCH 659/860] backends/igvm: add a tracepoint for qigvm_cleanup_memory Useful for debugging memory region cleanups. Signed-off-by: Ani Sinha Message-ID: <20260817142010.80693-6-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- backends/igvm.c | 1 + backends/trace-events | 1 + 2 files changed, 2 insertions(+) diff --git a/backends/igvm.c b/backends/igvm.c index 9b101c8182..7b7bdc72b7 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -1120,6 +1120,7 @@ void qigvm_cleanup_memory(IgvmCfg *cfg) QTAILQ_FOREACH_SAFE(imr, &cfg->memory_regions, next, tmp) { + trace_qigvm_cleanup_memory(imr->mr->name); memory_region_del_subregion(get_system_memory(), imr->mr); vmstate_unregister_ram(imr->mr, NULL); QTAILQ_REMOVE(&cfg->memory_regions, imr, next); diff --git a/backends/trace-events b/backends/trace-events index 009a25b0be..9b7b9b7d68 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -33,3 +33,4 @@ igvm_reset_hold(int type) "type=%u" igvm_reset_exit(int type) "type=%u" igvm_file_loaded(const char *fn, int32_t handle) "fn=%s, handle=0x%x" igvm_process_file(int32_t handle, bool context_only) "handle=0x%x context-only=%d" +qigvm_cleanup_memory(const char* name) "freeing mr %s" From b23f6d5294cc2b3bcf99c4c28b5b58b8ae5bc1f1 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Wed, 19 Aug 2026 09:41:05 +0530 Subject: [PATCH 660/860] hw/misc/vmlaunchupdate: add api header Add a separate header file for guest usable api definitions. CC: Alex Graf CC: Gerd Hoffman Reviewed-by: Alexander Graf Signed-off-by: Ani Sinha Message-ID: <20260819041105.110625-1-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- .../standard-headers/misc/vmlaunchupdate.h | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 include/standard-headers/misc/vmlaunchupdate.h diff --git a/include/standard-headers/misc/vmlaunchupdate.h b/include/standard-headers/misc/vmlaunchupdate.h new file mode 100644 index 0000000000..7f8382a765 --- /dev/null +++ b/include/standard-headers/misc/vmlaunchupdate.h @@ -0,0 +1,102 @@ +/* + * Guest driven VM launch state update device via IGVM. + * The definitions in this header defines the API for the hypervisor interface. + * For details and specification, please look at docs/specs/vmlaunchupdate.rst. + * + * Copyright (C) 2026 Red Hat, Inc. + * + * Authors: Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef VMLAUNCHUPDATE_API_H +#define VMLAUNCHUPDATE_API_H + +/* fw-cfg file definition */ +#define FILE_VMLAUNCHUPDATE "etc/vmlaunchupdate" + +/* version */ +#define VM_LAUNCHUPDATE_VERSION 0x01 + +/* format bits, used by both 'capabilities' and 'control' */ + +/* igvm */ +#define VM_LAUNCHUPDATE_FORMAT_IGVM (1ULL << 32) + +/* 'control' field bits */ + +/* disable vmlaunchupdate interface */ +#define VM_LAUNCHUPDATE_CTL_DISABLE (1 << 0) +/* revert to the original host provided igvm */ +#define VM_LAUNCHUPDATE_CTL_HOST_IGVM (1 << 1) + +/* The combination of the above two ctl interfaces work as + * follows: + * + * A) CTL_HOST_IGVM=off CTL_DISABLE=off + * + * Supplied IGVM file replaces the firmware permanently. Updating the + * firmware again is possible. + * + * B) CTL_HOST_IGVM=off CTL_DISABLE=on + * + * Supplied IGVM file replaces the firmware permanently. Updating the + * firmware again is not possible. + * + * C) CTL_HOST_IGVM=on CTL_DISABLE=off + * + * Supplied IGVM file replaces the firmware for one reset. Resetting + * again will switch back to the original firmware. Updating the + * firmware again is possible. + * + * D) CTL_HOST_IGVM=on CTL_DISABLE=on + * + * Supplied IGVM file replaces the firmware for one reset. Resetting + * again will switch back to the original firmware. Updating the + * firmware again is NOT possible. + * + */ + +/* status code */ +enum VMLaunchUpdateStatus { + VM_LAUNCHUPDATE_SUCCESS = 0, + VM_LAUNCHUPDATE_LOAD_FAIL = 1, + VM_LAUNCHUPDATE_NOT_IGVM_INIT = 2, +}; + +typedef struct QEMU_PACKED { + /* api version */ + uint16_t version; + + /* + * The guest can read this in order to determine if loading new IGVM + * succeeded. + */ + uint16_t status; + + uint32_t _padding; + + /* VMM capabilities, read-only. */ + uint64_t capabilities; + /* control bits, see VMFWUPDATE_CTL_* */ + uint64_t control; + + /* + * address and size of the IGVM image. Will be cleared when + * the write completes successfully and IGVM file is correctly parsed. + */ + uint64_t fw_image_addr; + uint64_t fw_image_size; + + /* + * address + size of opaque blob. The guest can use this to pass on + * information, for example which memory region the linux kernel has been + * loaded to. writable, will be kept intact on firmware update. + */ + uint64_t opaque_addr; + uint64_t opaque_size; + +} VMLaunchUpdate; + +#endif From 30688c02a76e50e4da779a99a7316c49cbdd840e Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 17 Aug 2026 19:50:02 +0530 Subject: [PATCH 661/860] hw/misc/vmlaunchupdate: Introduce hypervisor fw-cfg interface support VM launch update is a mechanism where the virtual machines can use IGVM file bundle to boot into a trusted execution environment without having to depend on a untrusted party to provide the IGVM bundle or firmware image. This is particularly useful for confidential virtual machines that are deployed in the cloud where the tenant and the cloud provider are two different entities. In this scenario, virtual machines can bring their own trusted IGVM file containing a trusted firmware image bundled as a part of their filesystem and then use this hypervisor interface to update to a trusted and deterministic boot state. This also allows the guests to have a consistent measurements on the firmware image. Currently, this mechanism only works if the VM was started with IGVM in the first place. This change introduces support for the fw-cfg based hypervisor interface and the corresponding device. The interface is made generic enough so that guests are free to use their own ABI to pass required information between initial and trusted execution contexts (where they are running their own trusted boot state) without the hypervisor getting involved in between. Currently, this device is only supported for x86_64 machines. Presence of IGVM host libraries is also required for parsing IGVM files. Hence, the device cannot be initialized for other machine types or hosts where IGVM support is not present. Trying to initialize it for arm for example will lead to failure: $ ./qemu-system-arm -device vm-launch-update -machine virt qemu-system-arm: -device vm-launch-update: This machine does not support vm-launch-update device A document detailing the specification is added in a subsequent patch. Please see docs/specs/vmlaunchupdate.rst. Functional and qtests are added in a subsequent patch. CC: Alex Graf CC: Gerd Hoffman Reviewed-by: Gerd Hoffmann Reviewed-by: Alexander Graf Signed-off-by: Ani Sinha Message-ID: <20260817142010.80693-8-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/misc/meson.build | 3 + hw/misc/trace-events | 6 + hw/misc/vmlaunchupdate.c | 333 +++++++++++++++++++++++++++++++ include/hw/misc/vmlaunchupdate.h | 38 ++++ 4 files changed, 380 insertions(+) create mode 100644 hw/misc/vmlaunchupdate.c create mode 100644 include/hw/misc/vmlaunchupdate.h diff --git a/hw/misc/meson.build b/hw/misc/meson.build index e86d9ad6b3..858ca845a2 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -164,6 +164,9 @@ specific_ss.add(when: 'CONFIG_MIPS_ITU', if_true: files('mips_itu.c')) specific_ss.add(when: 'CONFIG_RISCV_MIPS_CMGCR', if_true: files('riscv_cmgcr.c')) specific_ss.add(when: 'CONFIG_RISCV_MIPS_CPC', if_true: files('riscv_cpc.c')) +if igvm.found() + specific_ss.add(when: 'CONFIG_FW_CFG_DMA', if_true: files('vmlaunchupdate.c')) +endif system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c9a868b3ef..2d6d2238c5 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -442,3 +442,9 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u" iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x" iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x" iommu_testdev_dma_armed(bool armed) "armed=%d" + +# vmlaunchupdate.c +launch_update_write(void) "" +vmlaunch_reset_enter(void) "" +vm_launchupdate_finalize(void) "" +restore_host_x86_igvm(void) "" diff --git a/hw/misc/vmlaunchupdate.c b/hw/misc/vmlaunchupdate.c new file mode 100644 index 0000000000..afa2d278ae --- /dev/null +++ b/hw/misc/vmlaunchupdate.c @@ -0,0 +1,333 @@ +/* + * Guest driven VM launch component update (using IGVM) device + * For details and specification, please look at docs/specs/vmlaunchupdate.rst. + * + * Copyright (C) 2026 Red Hat, Inc. + * + * Authors: Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "system/physmem.h" +#include "system/reset.h" +#include "qemu/target-info-qapi.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/core/qdev-properties.h" +#include "hw/i386/pc.h" +#include "exec/cpu-common.h" +#include "hw/misc/vmlaunchupdate.h" +#include "system/igvm.h" +#include "system/igvm-internal.h" +#include "qemu/error-report.h" +#include "trace.h" + +/* returns NULL unless there is exactly one device */ +static VMLaunchUpdateState *vm_launchupdate_find(void) +{ + Object *o = object_resolve_path_type("", TYPE_VMLAUNCHUPDATE, NULL); + + return o ? VMLAUNCHUPDATE(o) : NULL; +} + +static bool vmlaunchupdate_supported(void) +{ + return target_arch() == SYS_EMU_TARGET_X86_64; +} + +static void init_vm_launch_update(VMLaunchUpdateState *s) +{ + s->launch_update.capabilities = VM_LAUNCHUPDATE_FORMAT_IGVM; + s->launch_update.control = 0; + + if (s->disabled) { + s->launch_update.control |= VM_LAUNCHUPDATE_CTL_DISABLE; + } + + s->launch_update.version = VM_LAUNCHUPDATE_VERSION; + return; +} + +static void clear_init_vm_launch_update(VMLaunchUpdateState *s) +{ + memset(&s->launch_update, 0, sizeof(s->launch_update)); + init_vm_launch_update(s); +} + +static bool no_igvmcfg(X86MachineState *x86m) +{ + IgvmCfg *igvmc; + + if (!x86m) { + return true; + } + + igvmc = x86m->igvm; + + if (!igvmc) { + /* The VM was not started with an IGVM, bail */ + info_report("guest was not initially started with IGVM, " + "not changing launch state."); + return true; + } + return false; +} + +static int process_x86_igvm(VMLaunchUpdateState *s, + uint64_t fw_image_addr, uint64_t fw_image_size) +{ + X86MachineState *x86machine = X86_MACHINE(qdev_get_machine()); + IgvmCfg *igvmc = x86machine->igvm; + IgvmHandle igvm; + void *image_addr_ptr; + hwaddr len; + + if (no_igvmcfg(x86machine)) { + return -2; + } + + if (!fw_image_addr || !fw_image_size) { + return -1; + } + + len = (hwaddr) fw_image_size; + image_addr_ptr = physical_memory_map((hwaddr) fw_image_addr, + (hwaddr *) &len, 0); + + if (!image_addr_ptr || (len < fw_image_size)) { + warn_report("vmlaunchupdate: Invalid guest addresses."); + goto err; + } + + igvm = igvm_new_from_binary(image_addr_ptr, fw_image_size); + if (igvm < 0) { + warn_report("vmlaunchupdate: Unable to parse IGVM file %" + PRIx64 ": %" PRIx64, fw_image_addr, fw_image_size); + goto err; + } + + /* free previous file context */ + if (igvmc->file >= 0) { + igvm_free(igvmc->file); + } + /* set new context */ + igvmc->file = igvm; + + physical_memory_unmap(image_addr_ptr, len, 0, 0); + info_report("vmlaunchupdate: new IGVM context set."); + + return 0; + err: + if (image_addr_ptr) { + physical_memory_unmap(image_addr_ptr, len, 0, 0); + } + return -1; +} + +static void restore_host_x86_igvm(void) +{ + X86MachineState *x86machine = X86_MACHINE(qdev_get_machine()); + IgvmCfg *igvmc = x86machine->igvm; + Error *errp = NULL; + + if (no_igvmcfg(x86machine)) { + return; + } + + /* free previous file context */ + if (igvmc->file >= 0) { + igvm_free(igvmc->file); + } + + info_report("restoring original host IGVM: %s", igvmc->filename); + igvmc->file = qigvm_file_init(igvmc->filename, &errp); + assert(!errp); + + info_report("vmlaunchupdate: host IGVM context set."); + + trace_restore_host_x86_igvm(); + + return; +} + +static bool fw_address_cleared(VMLaunchUpdateState *s) +{ + return !s->launch_update.fw_image_addr && + !s->launch_update.fw_image_size; +} + +static void launch_update_write(void *dev, off_t offset, size_t len) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(dev); + uint64_t addr; + uint64_t size; + int rc; + + s->launch_update.status = VM_LAUNCHUPDATE_SUCCESS; + + if (s->disabled) { + goto end; + } + + if (s->launch_update.control & VM_LAUNCHUPDATE_CTL_DISABLE) { + s->disabled = true; + goto end; + } + + if (fw_address_cleared(s) && + (s->launch_update.control & VM_LAUNCHUPDATE_CTL_HOST_IGVM)) { + /* restore host IGVM on immediate next reset */ + s->host_igvm_on_reset = true; + goto end; + } + + if (!(s->launch_update.control & VM_LAUNCHUPDATE_FORMAT_IGVM) && + !fw_address_cleared(s)) { + /* at least one address provided but the format is not IGVM */ + s->launch_update.status = VM_LAUNCHUPDATE_LOAD_FAIL; + goto end; + } + + /* process guest provided IGVM image */ + if (s->launch_update.control & VM_LAUNCHUPDATE_FORMAT_IGVM) { + if (target_arch() == SYS_EMU_TARGET_X86_64) { + addr = le64_to_cpu(s->launch_update.fw_image_addr); + size = le64_to_cpu(s->launch_update.fw_image_size); + rc = process_x86_igvm(s, addr, size); + if (rc < 0) { + switch (rc) { + case -2: + s->launch_update.status = VM_LAUNCHUPDATE_NOT_IGVM_INIT; + break; + default: + s->launch_update.status = VM_LAUNCHUPDATE_LOAD_FAIL; + } + goto end; + } + } + /* process other machines here when support is added */ + } + + /* clear the addresses */ + s->launch_update.fw_image_addr = 0x0; + s->launch_update.fw_image_size = 0x0; + + end: + trace_launch_update_write(); + return; +} + +static void launch_update_select(void *dev) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(dev); + init_vm_launch_update(s); +} + +static void vmlaunch_reset_enter(Object *obj, ResetType type) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(obj); + + if (target_arch() != SYS_EMU_TARGET_X86_64) { + return; + } + + if (s->host_igvm_on_reset) { + restore_host_x86_igvm(); + s->host_igvm_on_reset = false; + /* restoring host igvm enables the interface again */ + s->disabled = false; + /* clear the host IGVM ctrl bit */ + s->launch_update.control &= ~VM_LAUNCHUPDATE_CTL_HOST_IGVM; + } + + if ((s->launch_update.control & VM_LAUNCHUPDATE_CTL_HOST_IGVM) && + (s->launch_update.status == VM_LAUNCHUPDATE_SUCCESS)) { + info_report("vmlaunchupdate: next reset will use host igvm"); + s->host_igvm_on_reset = true; + } + + trace_vmlaunch_reset_enter(); +} + +static ResettableState *vmlaunch_reset_state(Object *obj) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(obj); + + return &s->reset_state; +} + +static void vm_launchupdate_realize(DeviceState *dev, Error **errp) +{ + VMLaunchUpdateState *s = VMLAUNCHUPDATE(dev); + FWCfgState *fw_cfg = fw_cfg_find(); + + /* multiple devices are not supported */ + if (!vm_launchupdate_find()) { + error_setg(errp, "at most one %s device is permitted", + TYPE_VMLAUNCHUPDATE); + return; + } + + /* if current machine is not supported, do not initialize */ + if (!vmlaunchupdate_supported()) { + error_setg(errp, + "This machine does not support vm-launch-update device"); + return; + } + + /* fw_cfg with DMA support is necessary to support this device */ + if (!fw_cfg || !fw_cfg_dma_enabled(fw_cfg)) { + error_setg(errp, "%s device requires fw_cfg", + TYPE_VMLAUNCHUPDATE); + return; + } + + fw_cfg_add_file_callback(fw_cfg, FILE_VMLAUNCHUPDATE, + launch_update_select, launch_update_write, s, + &s->launch_update, + sizeof(s->launch_update), + false); + + clear_init_vm_launch_update(s); + /* + * This device requires to register a global reset because it is + * not plugged to a bus (which, as its QOM parent, would reset it). + */ + qemu_register_resettable(OBJECT(s)); +} + +static void vm_launchupdate_finalize(Object *obj) +{ + qemu_unregister_resettable(obj); + trace_vm_launchupdate_finalize(); +} + +static void vmlaunchupdate_device_class_init(ObjectClass *klass, + const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + /* we are not interested in migration - so no need to populate dc->vmsd */ + dc->desc = "VM launch state update device"; + dc->realize = vm_launchupdate_realize; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + rc->phases.enter = vmlaunch_reset_enter; + rc->get_state = vmlaunch_reset_state; +} + +static const TypeInfo vmlaunchupdate_device_types[] = { + { + .name = TYPE_VMLAUNCHUPDATE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VMLaunchUpdateState), + .class_init = vmlaunchupdate_device_class_init, + .instance_finalize = vm_launchupdate_finalize, + }, +}; + +DEFINE_TYPES(vmlaunchupdate_device_types) diff --git a/include/hw/misc/vmlaunchupdate.h b/include/hw/misc/vmlaunchupdate.h new file mode 100644 index 0000000000..02f96cebdc --- /dev/null +++ b/include/hw/misc/vmlaunchupdate.h @@ -0,0 +1,38 @@ +/* + * Guest driven VM launch state update device via IGVM. + * For details and specification, please look at docs/specs/vmlaunchupdate.rst. + * + * Copyright (C) 2026 Red Hat, Inc. + * + * Authors: Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + */ +#ifndef VMLAUNCHUPDATE_H +#define VMLAUNCHUPDATE_H + +#include "hw/core/qdev.h" +#include "qom/object.h" +#include "qemu/units.h" +#include "system/igvm-cfg.h" +#include "standard-headers/misc/vmlaunchupdate.h" + +#define TYPE_VMLAUNCHUPDATE "vm-launch-update" + +typedef struct VMLaunchUpdateState { + DeviceState parent_obj; + VMLaunchUpdate launch_update; + bool disabled; + bool host_igvm_on_reset; + ResettableState reset_state; +} VMLaunchUpdateState; + + +typedef struct VMLaunchUpdateStateClass { + ObjectClass parent_class; +} VMLaunchUpdateStateClass; + +OBJECT_DECLARE_SIMPLE_TYPE(VMLaunchUpdateState, VMLAUNCHUPDATE); + +#endif From 76c16b3c37e705218844264e48b3657d14529d23 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 17 Aug 2026 19:50:03 +0530 Subject: [PATCH 662/860] docs/spec: Add a specification document for vm-launch-update device This change adds a specification document and expanation for the vm-launch-update device. CC: Alex Graf CC: Gerd Hoffman Reviewed-by: Alexander Graf Signed-off-by: Ani Sinha Message-ID: <20260817142010.80693-9-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- docs/specs/index.rst | 1 + docs/specs/vmlaunchupdate.rst | 198 ++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 docs/specs/vmlaunchupdate.rst diff --git a/docs/specs/index.rst b/docs/specs/index.rst index b7909a108a..3cdf242661 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -34,6 +34,7 @@ guest hardware that is specific to QEMU. virt-ctlr vmcoreinfo vmgenid + vmlaunchupdate rapl-msr rocker riscv-iommu diff --git a/docs/specs/vmlaunchupdate.rst b/docs/specs/vmlaunchupdate.rst new file mode 100644 index 0000000000..2f1e95afbf --- /dev/null +++ b/docs/specs/vmlaunchupdate.rst @@ -0,0 +1,198 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +VMLAUNCHUPDATE Interface Specification +###################################### + +Introduction +************ + +``VmLaunchUpdate`` is an extension to ``fw-cfg`` that allows guests to replace +boot state in their virtual machine using IGVM file container. Through a combination +of this ``fw-cfg`` hypervisor interface, an IGVM file containing specific directives +and with hypervisor stack knowledge, guests can deterministically replace the launch +state for guests. This is useful for environments like SEV-SNP where the +launch payload becomes the launch digest. Guests can use vm-launch-update device to +provide a measured, full guest payload (BIOS image, kernel, initramfs, kernel +command line) to the virtual machine which enables them to easily reason about +integrity of the resulting system. +It is also to be noted that this mechanism currently works only when the guest was +already started with an IGVM file defining its initial launch state. Subsequent +guest resets will use the launch state as defined in the guest provided IGVM file, +not the file with which the guest was initially started. If the guest was not started +with IGVM, writing a new bundle through the ``fw-cfg`` interface has no effect. + +For more information, please see the `KVM Forum 2024 presentation `__ +about this work. + + +.. _KVMFORUM: https://www.youtube.com/watch?v=VCMBxU6tAto + +Base Requirements +***************** + +#. **fw-cfg**: + The target system must provide a ``fw-cfg`` interface. For x86 based + environments, this ``fw-cfg`` interface must be accessible through PIO ports + 0x510 and 0x511. The ``fw-cfg`` interface does not need to be announced as part + of system device tables such as DSDT. The ``fw-cfg`` interface must support the + DMA interface. It may only support the DMA interface for write operations. + +#. **IGVM support**: + The hypervisor must provide support for parsing and executing the IGVM file bundle. + +#. **Confidential guests**: + For confidential guests, the hypervisor must support guest reset. Otherwise, the new + boot state provided through IGVM will not be applied. + +The Fw-cfg File +*************** + +Guests drive vmlaunchupdate through special ``fw-cfg`` files that control its flow +followed by a standard system reset operation. When the ``vm-launch-update`` device +is available, it provides the following ``fw-cfg`` file: + +* ``etc/vmlaunchupdate`` - It exposes a structure of the following type, all in + little-endian format: + +.. code-block:: c + :linenos: + + typedef struct { + uint16_t version; + uint16_t status; + + uint32_t _padding; + + uint64_t capabilities; + uint64_t control; + + uint64_t fw_image_addr; + uint64_t fw_image_size; + + uint64_t opaque_addr; + uint64_t opaque_size; + + } VMLaunchUpdate; + + +Currently, the ``version`` number (line 2 above) is initialized to the value ``1``. +Only IGVM files are supported at present. The ``capabilities`` (line 7) and ``control`` (line 8) both support +the following single value: + +* ``VM_LAUNCHUPDATE_FORMAT_IGVM`` + + This value is used by the hypervisor to indicate that only IGVM container files are supported. + This is set as a part of ``capabilities`` parameter (line 7) in the above structure. This same value + is passed by the guest to the hypervisor in the ``control`` parameter (line 8) in the above structure + to indicate that the guest passed IGVM file in memory to the hypervisor. The starting guest physical + address of the IGVM file in memory is specified in ``fw_image_addr`` and it's length is specified in + ``fw_image_size`` by the guest. If any other value is passed by the guest in the ``control`` parameter, + the write is ignored by the hypervisor. + +Following ``control`` parameters are supported: + +* ``VM_LAUNCHUPDATE_CTL_DISABLE`` + + This value is set in the ``control`` parameter by the guest in order to disable this ``fw-cfg`` + hypervisor interface from further updating the guest launch state with a new IGVM file. + +* ``VM_LAUNCHUPDATE_CTL_HOST_IGVM`` + + This value is set in the ``control`` parameter by the guest in order to send request to the + hypervisor to initialize the guest using the original host provided IGVM file. + It is useful if the guest wanted to update the UKIs present in the ESP and upon + reset, use one of the updated UKIs present there. If the guest passed addresses in memory + where its own IGVM file is loaded (see below) while also setting this control value, the next + reset will load the guest provided IGVM file and a subsequent second reset will restore the original + host IGVM. If the guest did not provide any addresses of its own IGVM (the address values are + cleared) while setting this control parameter, the immediate next guest reset will load the + original host provided IGVM file. + + The combination of the above two ctl interfaces work as + follows: + + A) ``CTL_HOST_IGVM`` = off ``CTL_DISABLE`` = off + + Supplied IGVM file replaces the firmware permanently. Updating the + firmware again is possible. + + B) ``CTL_HOST_IGVM`` = off ``CTL_DISABLE`` = on + + Supplied IGVM file replaces the firmware permanently. Updating the + firmware again is not possible. + + C) ``CTL_HOST_IGVM`` = on ``CTL_DISABLE`` = off + + Supplied IGVM file replaces the firmware for one reset. Resetting + again will switch back to the original firmware. Updating the + firmware again is possible. + + D) ``CTL_HOST_IGVM`` = on ``CTL_DISABLE`` = on + + Supplied IGVM file replaces the firmware for one reset. Resetting + again will switch back to the original firmware. Updating the + firmware again is NOT possible. + +``fw_image_addr`` (line 10) is the base guest physical address of the guest memory where the IGVM file of size +``fw_image_size`` (line 11) is loaded. ``opaque_addr`` (line 13) and ``opaque_size`` (line 14) are used by +the guest for passing data across resets. The contents of this guest memory are preserved across the +reset. For confidential guests, this memory region must come from guest shared unencrypted memory. + +``status`` (line 3) is written by the hypervisor and it indicates the result of the IGVM loading operation. +A success indicates status code 0. Otherwise a non-zero status code indicates failure. The nature of the +failure is indicated by the value of the code. + +Triggering the Launch State Update using IGVM +********************************************* + +To initiate the launch update process, the guest issues a standard system reset +operation through any of the means implemented by the machine model. + +On a write to the ``etc/vmlaunchupdate`` interface, the hypervisor evaluates whether this +hypervisor interface is disabled. If it is, it ignores any writes to this ``fw-cfg`` file +by the guest. No updates to initial launch state is performed. + +If the hypervisor interface is enabled, upon write to the ``etc/vmlaunchupdate`` interface, +the hypervisor parses the IGVM file bundle passed to it in memory, with starting guest physical +address at ``fw_image_addr`` and length ``fw_image_size``. If parsing is successful, it creates +a context handle to the IGVM file. If parsing and context loading is successful and there are no +errors, ``fw_image_addr`` and ``fw_image_size`` are cleared. The guest can check this in order +to determine if the IGVM was successfully parsed and the new context was loaded. If not, the +guest can throw error and abort rebooting to new IGVM boot state. Alternatively, the guest can +also check the ``status`` code from the ``fw-cfg`` file. A status code of 0 indicates success +of the operation. Non-zero status code indicates failure. Exact nature of the failure is +indicated by the value of the code. Currently, only two error values are supported: + +* ``VM_LAUNCHUPDATE_LOAD_FAIL`` - defined as value 1 and is set when loading of the IGVM file failed. +* ``VM_LAUNCHUPDATE_NOT_IGVM_INIT`` - defined as value 2 and is set when the guest was not started with + IGVM file. + +Upon guest reset, the hypervisor executes the IGVM bundle using +the context handle, setting the initial launch state of the guest accordingly. +If an invalid IGVM file is passed, parsing the file fails and the hypervisor ignores it +when ``fw-cfg`` files are written. In this case, the initial launch state +is not modified. If invalid addresses are passed, the hypervisor ignores them as well and no +new launch state is set. + +The launch state update mechanism works both for confidential and non-confidential +guests. In confidential guests, as a part of the reset operation, all existing +guest shared memory (shared with the hypervisor) as well as the guest memory region +starting with ``opaque_addr`` and length ``opaque_size`` are preserved. +The reset causes recreation of the VM context which triggers a fresh +measurement of the replaced BIOS region and reset CPU state. + +For non-confidential guests, there is no concept of guest private memory and all the existing +guest memory is preserved (this is the default behaviour today - QEMU does not reset/clear +guest memory upon reset). + +In both confidential and non-confidential cases, CPU and device state are reset to +the reset states specified in IGVM. In confidential environments, the guest +always resumes operation in the highest privileged mode available to it (VMPL0 in SEV-SNP). + +Closing Remarks +*************** +The exact content of the memory region specified by starting address ``opaque_addr`` +and length ``opaque_size`` is guest specific and is hypervisor agnostic. The hypervisor does +not care about the contents of this memory region. Therefore, it is not included in this +specification. As of writing this document, TDX guests on QEMU does not support IGVM. +Therefore, this mechanism cannot be used to change launch state of TDX guests. From f0e7be2e8b647c43dad3b3cabe3b50b071205351 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 17 Aug 2026 19:50:04 +0530 Subject: [PATCH 663/860] tests/qtest: Add small igvm files for testing purpose Needed by launchupdate-test.c. README file is added with explanation on how to build these IGVM files. CC: Alex Graf CC: Gerd Hoffman Reviewed-by: Alexander Graf Signed-off-by: Ani Sinha Message-ID: <20260817142010.80693-10-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- tests/data/igvm/README | 45 ++++++++++++++++++++++++++++++++++ tests/data/igvm/hello.igvm | Bin 0 -> 137112 bytes tests/data/igvm/qemuinit.igvm | Bin 0 -> 137112 bytes 3 files changed, 45 insertions(+) create mode 100644 tests/data/igvm/README create mode 100644 tests/data/igvm/hello.igvm create mode 100644 tests/data/igvm/qemuinit.igvm diff --git a/tests/data/igvm/README b/tests/data/igvm/README new file mode 100644 index 0000000000..d4d26478ee --- /dev/null +++ b/tests/data/igvm/README @@ -0,0 +1,45 @@ + +=== This is the readme file for all IGVM file bundles provided === +=== All IGVM files are covered by GPL v2 or later === +SPDX-License-Identifier: GPL-2.0-or-later + +Small IGVM file bundles required for tesing vm-launch-update device +=================================================================== + +This directory contains IGVM files required for launchupdate-test.c. +These IGVM bundles can be built from the following repository: + +https://gitlab.com/anisinha/virt-firmware-rs + +Just type "make" in the top of the repository. The built IGVM files +can be found in the target/x86_64-unknown-none/debug directory. + +$ ls -l ./target/x86_64-unknown-none/debug/*.igvm +-rw-r--r--. 1 anisinha anisinha 153624 Jun 8 10:05 ./target/x86_64-unknown-none/debug/hello.igvm +-rw-r--r--. 1 anisinha anisinha 157848 Jun 8 10:05 ./target/x86_64-unknown-none/debug/igvmtest.igvm +-rw-r--r--. 1 anisinha anisinha 987384 Jun 8 10:05 ./target/x86_64-unknown-none/debug/mefisto.igvm +-rw-r--r--. 1 anisinha anisinha 368312 Jun 8 10:05 ./target/x86_64-unknown-none/debug/qemuinit.igvm +-rw-r--r--. 1 anisinha anisinha 223944 Jun 8 10:05 ./target/x86_64-unknown-none/debug/snptest.igvm + +The IGVM bundles for sev-snp testing are also kept in a seperate directory /snp_igvm_bundles in the +above repository for use without requiring to build from source: + +$ virtfirmware-rs/snp_igvm_bundles$ ls -l +total 388 +-rw-r--r-- 1 anisinha anisinha 199176 Aug 7 00:58 snptest.igvm +-rw-r--r-- 1 anisinha anisinha 195048 Aug 6 06:10 snptest-nohello.igvm + +Following files are used by the tests/qtest/launchupdate-test.c for non-coco case: + +hello.igvm +qemuinit.igvm + +For confidential case (that is when COCO=1 is passed in the environment), the following +IGVM bundles are used: + +snptest-nohello.igvm +snptest.igvm + +The IGVM bundles used by the confidential case are not checked into the QEMU +repository. Users who wish to test the confidential case can use the IGVM +bundles from https://gitlab.com/anisinha/virt-firmware-rs as mentioned above. diff --git a/tests/data/igvm/hello.igvm b/tests/data/igvm/hello.igvm new file mode 100644 index 0000000000000000000000000000000000000000..2d3ffda5b157c13d109ef965d1c0f41e9a34caf5 GIT binary patch literal 137112 zcmeEv3w&Hvwf9V#X+uky01XX@bkI>!9x_oXDKs#Fl%8M$1GZ9BFp88SSB#L*UICe! zRC74YH7W{TMdG_Y(5QtL#4@4S7bpRgw=kfRuKw?V)$?QIib+iGb=(Lp7z8RJcdHmp-|Xdd{?8q|F4KwD|k%76AGSG@RWkLD|m;3rxm z|LLOsOK1SER`8gDCloxX;3)-fSMUx6Pb+wzg7+)yMlKp zcv`{x6ue)-2NXQ3;6nK631@BPsw1W33c)x-V zD0o)EhZH=g;3Eni`ktbH1&=9sLcx;?o>K631@BPsw1W33c)x-VD0o)EhZH=g;3Eni z`o5xn1&=9sLcx;?o>K631@BPsw1W33c)x-VD0o)EhZH=g;3Eni`hlW<1&=9sLcx;? zo>K631@BPsw1W33c)x-VD0o)EhZH=g;3Eni`k|tK1&=9sLcx;?o>K631@BPsw1W33 zc)x-VD0o)EhZH=g;3Eni`jMi41&=9sLcx;?o>K631@BPsw1W33c)x-VD0o)EhZH=g z;3Eni`mv&a1&=9sLcx;?o>K631@BPsw1W33c)x-VD0o)EhZH=g;3EniTBYb;!D9-Z zQ1GOJrxd(h!8;T@t>AqM-ml;T3Z7N)AqCGV_=tjsexm4K!D9-ZQ1GOJrxd(h!8;T@ zt>AqM-ml;T3Z7N)AqCGV_=tjseyZqS!D9-ZQ1GOJrxd(h!8;T@t>AqM-ml;T3Z7N) zAqCGV_=tjsex~SO!D9-ZQ1GOJrxd(h!8;T@t>AqM-ml;T3Z7N)AqCGV_=tjsey-?W z!D9-ZQ1GOJrxd(h!8;T@t>AqM-ml;T3Z7N)AqCGV_=tjsu2b}{;4uYHD0ouAQwrX$ z;2jE{R`5Or?^o~v1tFd_=)RzfknA;4uYHD0ouAQwrX$;2jE{R`5Or?^o~v z1tFd_=)Rzf|tFd_=)R z1B(6?Jf`3Y1y3q?O2OL|yhFj$3f`yS{R%#y;8_J9Qt+IDk0^NPSBm}>Jf`3Y1y3q? zO2OL|yhFj$3f`yS{R%#y;8_J9Qt+IDk0^NP*NXlXJf`3Y1y3q?O2OL|yhFj$3f`yS z{R%#y;8_J9Qt+IDk0^NPH;VohJf`3Y1y3q?O2OL|yhFj$3f`yS{R%#y;8_J9Qt+ID zk0^L(wW5Cok12RU!IKJ}Qt);K?@;ixg7+zSzk&}acviuO6g;QkBMKh6UeUjT#}quF z;7J8fDR{ercPMyT!TS`vU%>|yJgeYC3Z7H&5d{z3py*%0V+x*7@T7vL6ue!*I}|*v z;C%|-uiyg;o>lN61Clx%U;Oz?Dq2Or+?^E!81s_oGtbz|Icuv7b6g+g3qJIUCDR@G`lM0?v@OB07 zQ1G;Z_bGV4f)6NoR>6l9Jg49z3Lg5cqJIUCDR@G`lM0?v@OB07Q1G;Z_bGV4f)6No zR>6l9Jg49z3Ld&y(Z7Pn6g;8eNd-?Sc)NmkD0o`I`xLxi!3PvPtKdTlo>TA<1rPmB z(Z7Pn6g;8eNd-?Sc)NmkD0o`I`xLxi!3PvPtKdTlo>TA<1rM!L^snGC1y3k=Qo&OS z-mc&s3Z7Q*J_YYr@BszSD)^9s=M;QI!9%ww`d9Fnf+rL_so*IEZ&&aR1y3t@pMv)* z_<(|E6?{m+a|%A9;GvN*8bBETUHv671^H8Y|383VH31$A;O13>^74rQZk8g-@nits zI05}s0Kabnygh(FH38lcz_(3+rvvyDO2Paqw!bfc&zu1758ww)fDZ)lBPYPK0ery( z_)q{pZ2~+Oz|WZg9|_>gCcs0_2lPJyULC-%ngEXl@E=crCj$7I3Gie9-#7uD3gGum zfVT(mrzXHV0{FHG@N@t_$Jc(~`xku*-ml;T0enhDv4Wy~Wdry`^$#g{PQgbMJoG|I z{}a_;t>7^QPbhd&!BYy}uHYRCo>uTa1@Bkz0R_)0_>h9<6nsR%LoX`&SMZpECloxX z;3)-fSMUx6Pb+wzg7+)D|k%76AGSG@RWkLD|m;3rxmlN61cIZg10Mphk~aSyidXV6?{O!vkE?>;5h{!3E<0$+mE|$c1d8D1a?Va zmjrf6V3!1TNnn=*c1d8D1a?Vamjrf6V3!1TNnn=*c1d8D1a?Vamjrf6V3!1TNnn=* zc1d8D1a?Vamjrf6;QuQL$nN;J?hMKGYxepr^UeH!XG3R&P=%bdFjU{OBpTkQV{FM< zzLFe$M|R2Ys&TXA>HYA&aMi!FVoNI;%dQG{Muw+>{_u*ikhAf&P$(O6>c`yY@=;`Z zsqoJ`0{@&9&(u|hLXH=C@X=7{mPyDlHiV}?;mOHd60aH?s?XME-3_;}0r@ukb=(ce z@Y_-bJY64nBHZ|MCU}wSa1nYvlHulfH3%Rf(|Fc%q0qYBN%Lj=F*MI9&*VkUEKd;1 zr!vipd=M9*e6Y<^ime)+Uo&Jl^2lG9!_?zN?hiakd+ua{7a783Y^bn3U`}_SJxd*TAsXV2I3641Hpiuf z-X**?$7}hg!ErB%C)r|$cZ+$~4w5xSM92{%l{zF%wQY`}QAkfX-oki7x>NmTr~8$V z6Kx)Jeu5^2IuCGANv-1{*X5z!=J;vR8^DCm=Wig^rB*w<;e6g=Qh2@jIBSHck z&omsLZ>pZ4Jo#%nUJmZ8H62 zIQ@7y8NFd^^oF~mH*9W>-jD^myIFQoWODDq_>{|WjfaUDsqTk_DF6?e7m9v88xP|) z0;-kaLajyKJq`&Wz4G#f@kmfoMSC~xofik4C>g6ZZNB_r%GOdUijMN>9ZL2-@3<(j z1^k1>J&HxskAczWqc;qp9j6F^V##s!K}9HK<@=tAvPuC{A1jmYXo`^fq=}2be_k!l zfQIMZ98dGXYrO73=^;-(B=+JpUc=|iobx{w1{%3fk|D{S_&1RMa&uV~?y4vpf{vLQ zVdm-IMdsE_D9+saPO?wx6T=hKq$AyPo$2Gf1f&laMeWEy{Vu85)&HJg^8ld%V7 zrq5;rKxr{s)xHnw!_!heb(p8-_;PxI82FecoFntt&~PnAUg)pdabGC9RC=P4H@$=Y z>Hl|px%77aX>hy+5V$zgW@x562cI+=?mC1jgY;VIEO(=Hk*k%im`c1W&mxdgb9`}O zKgXM1{j6wmPhOCtj!2>~kL9byJ;F+7EDeWlq7!!9d-FVh$SQzZ((Np>v{V}YV_9e` z{)D?&!!Q0WHT;s7QVkUap#lSi$A=b|!*v?{jXLh@jhEfeEN?|!RiuM9EM&JFp=seN zg1CeYpltA%v?CCj4URsB$}f>&=*;_RmC_;iVnga6wovrS>=9#


b%X`Syga=^G9 z*;lw{Gij+a%^+K8T2=#s>kdbRQhztB9e4t^I#$@10*X0n`G9|y)!*%GgQt^Gj^aBS zcNbL@uCa}WheG@R4ek5do;H z#-|&5;{C8Hv9QdXXN_-}buW<2&4P%!>X|QvOg7&JlW*IfO+F&b7hU-r?uKu~ANa#z zpzw!xzf6DF6X)+J!?CQlWmv0|SuM%dB>tPhl;Q4Hr~B3p#~pOK2bVhC*>=ahGR_}j za@;H8YvtM#&+_W6HtAmE9b_8yq?runM{%VGv?<6a1%XrvyFnyt%P@j7Wmwwru86b6 ztaOOapuS1cdxQpY#@)no7Qf%(5(@%1vEmhR3_Vgl&ugZ!?-^XY$N_)i(`uoLdO=@? z0W`MH;>~}AeB|q9J4~#$`1Hd`2h~dm3LPfc~{2! zna7KKNa{Dm;pq3_(1j8q7#uQ!wK)%iVPu>x8E+QEX!=2982*8fYaF{rG@ajOG`(0- zBM%^zilB9{kxZUg(~FI!;YhO%HQmkr;@j8i2|1isfrTa+=>d4{hD>ZM^Mv7@Y z@lmEhsVTWeR!E(uIFx#bRq8-VsZ;QwHz>71GTs~~vZGQD28!;x_g16SDr7>LN?V2| z!iF(rShw7eotKl6iPkjC8N}}hWO#O%m5$JFeXo| zR1ER-$uTGu#82>f%!=gN@?@ca zY+-?ow?>Yp!uvjVJ%gERoL&6-W$-(f9y0{(bzxD0~)BXuS0W zqw%G}L8Q@^p_Q=JOcsqthChdZi4|W>vvN5QR~w}x^t&?N!2%HSa=Bt`2UKVv|5}?2!xO4k z#QS-L`a9$b=Pl{*j20iTc-rF2Elx^Qf7arC7VqUX)427ogd^YI!Y5chQjPjy=A6}J z8WSH8_1E8STEO)ys2|Am?Bgf|mNtaUY)l5 zP`_DC!Te*zGZJ}VqJ+`X1WoyK_GEEM5vO@e8htYV-RWea$3{t4nviFkd8TLR!eqa( z=fl#5ciA>TdUOXU)gf0-(J)(bm>*-uR;~qlk%NT4OX663z3WSXZ5A8;%in3kGJHqwLK0U@;4nMmDQmjlr&~Zv2b~pz67zQF)0|pj zB*o%{lVN*thnTJtX9NNSxq_CRh zq}4$;$8tp03W5Pvu5fI;0?*17I^z|pH*OK*H=i?&i@9FoMY;g9c^$@-Vl<^Xz%{3f z8lD79=C~3BhJj4)${BhXA;pyL$JI9_K$4a(roq!N4HI?;wOuQ&>W z&jN)T`S3HwjZl3k^8qPi)aGPX%f!ZaBjh~EMX1Fl?>3va7LQ~bqmTfl=}a}BVNF%M zW*U!vKf+#Js_clT3MobItu7nTQTCT8s+J|?RVvxWzS zpmg+_>rNAPH-PvpRY)h#EP^KIC0kM~D8|%S^oCI|vPGIEi;piMOYfEOf8cU?Pn=J( zyfGIKOvCczR$L&rt7Nxi`YhCjwVsrzbYBoEu1GTD*ifn9HkNOWAC8sa!?}oh=pLQP z0@sCS9{Tpq;|lEh&wogGKXrq!%{38754FvyHn!RXfc-Hc>R(iN2ko*p|G8JOaI%lw zqcxS%8VpX9BCLuz);(Y1O1=?8)yo(KXfbKa^YNE0Fi#W&QvJESFq0V{gQTwdu(Xp{gOa_jBok3vkEw4m~)kL z;3lk#joV>Cf0Diqb}3K(BkPn3|51iULe|%Ik50YtOs4h|&Fl`XM%FHbKr-_-L1ce0 zLUA|d*H3262>O_Bnw2-6SquHLp$d+&VP>O>ow=JXSWtL7G(z$w)saaquTMl|fGhjEKfXDU#kJYAFOOeNR%j1+1kNX#S+{^MPy;9RY-Ajo_ z(5?ACY2qBfU>T2#C*m;`;PF39u^*xsHBOgkewA;&L-jT}dJj>p;nkDyz#x2deToJWi$JMvf^ z;PGLyf^!8$9;+>n_ncJZF~82&HD(Utek2yv`%D#ioNM|u=+=D3RK}l*3FsQJWr@dC ztN~ML2xC3e+-OQ%Bph&#m2zATz*A1Q#{@9`K@Uh`fS7-F6hR6{XpH(XXYOd{G|rBy zKr>XpMis|&ZW}QT6`RV%$bWLD<*eO%o$#iyTyz=+ z;8}N34P*r^iLrUZb+qtIq(wGQd<26NqqYZ;?-^ua z8L~T(UW1%ahLD58XHiMAESaPVA4c+#WyzBJcqG@CB{PCA9E#-s#JXll^5nEogXFvI zQeMeeILykl4YO`@O5i&;v4=~9S!SKGpDI=f`^4l(;!^1XoB>N;%FMYQvcs(Brymec zat}z|MCCdeY%p7*#p6KSpVjCsGxR=Z=yj9c{;x|fg#&$#i;nG8#@S=>Y=U?D?Dt>r z&yzH<`;CNd31M+=Y%Cgb-bugl#RiHF6Chb4jLZ~q*yc(wUCqUY1b3s+$+;zsyIN<( zW1s*pLLs^{m$)Za!H<*+5d7qV{2~6P+h9$GrTQ#qQnmG4Z?Y5opWbMO3ueG%q}EUW z??5^{4!qcxSc3oHFiDhtB_+<#yGn>$i>#oxUFOSkB7f#YzXv79t9P z=UGb6)?wTm9z{=!`PlAwUlxMyCMP^Z2`1>-qjYNtr;*UICYiJTvqkFLu-3Fetc&*I z^_ucL?%?11w5mz#(}k%xVQ_4C_-eKXh9N(xnEdu6rr_IvC#{4E_y^ zOO;Z&t=H5AGeK3eh12s)iT#5mcE|W21;|QBvRz@a{bLOqgz5RQ(n*#VT{z?kQI1l4 z)sA}v>~Ok!1NAE{B|%6@4ta=i)D{cc1;Yu-KY+U4O8L##zm@Wt>HE%kYvng%0pefh zKQn!pgZP`1pL8Z_K+YGgf&$)XzT5&ccJGo?P}@akGSNqkC?B~Ph3m%?u`P!qFGdqm3sjWdcsysDzEcsBw{8uuss8*Jm+)b0~(h#0|kZ4(SOF zKNMZ_xHeTeHrEKF3ko<|uDCGxd z2zt@v{GAV)kO!TO%b@ggYw2=!I9NK{@hF{6I!5LU^7z7P!y{I?W)+Z$8M&}xqMJF# zpCBHiGLQ@1Y?gF0w8`){!?XvS&A@;1fJ(5*vtld_elC~};m>FcAriV^zki+L6#4CO%u4LnvLO9?oK{u@(T0uZdUq>;DP zGizu(2)3Bkog$S!Qt{?|RSK*&D>R;;>>oF}grfv^!ckK{3xJnZQqV3Dfn*0)G9(cX z%h_tsXN-(HR7WGn9Y}EWz#S-3>bUFRU6OapdFQZO#8?G_Fto67Id`VK$dSM16ReHh z^ci8Os@yi%iD&`TbAx}A^luy*0il$CvzP|3qcPP{*U&pOYD|U{daH)7#Rcgwxt%jD z=5+_VA3HX^IgQiDmH6+I?%?3?xtSr2C8p;oek5uQLgjx->Xu8UHfsxa=zd7t{GG_HxHVa(8ge5%G?(1a^62 z)i(G@@Uelt7z&Rxqr6c$^xlqy_ep|X#Pb^Cc$Cg`BULuPKHKbW$VcxU8?usVMaT||oFU(9O7{$ctO z)kyD%EFvlRU|Ki=9WT;~1j;vZbO3?-sW$K&AKpI*)6<|P2%aSN1+)&*OADR70~rOL z>XZ1&b)~vj+(zvM3;_Cv&LsM0gy==Ok(*44cQnJza!Ua3#xNZ`fRPEs#y1j>L0(2U z(j0R?{+HOQ;TAmJocImszt4a6R}8w#B5c^#Vzh}YIouzr;)8cfujuV&b0{fOa}2sZ zXdMn(>*)2#m;}9|fgXF7=}I}vgn)eR(1rV#R3zLj5dIlHzP)fR{eFspd(A(O6~cc+ zi|aSLcS?x3fJGNy-V#RtJ>k%2I+w}5&Vob#9_`_r(mp*K?b!$3#6CS2?O9xUS`zKa zFsBSbDOB?dQ_VT3M&2_L4W9Bdqo;O0!Jf$Yrsx+QfCPr05HZk$ddbC0u)6d&OcNz! z#sLIi`Il<>EOQ&k&FMf?Ze(Ux=;@G2?Oot%csl-IP(Fc8IMiqJghRpf*@Qc|G6Fpo zEBkS&vLq|}l3E$(6VWf+N^1P^%ra`{))>~M2B?_c+wnR&^40qlbB<@8D|pc_TupkH zj;Gf$jvfaAp_j!*jjee+JuD`H-ua}5MP@%&EB%*bmAOI;OB1lN&Cm-}vf?%xCbrxH zf?0=&vDHF?f;oRCpI=7{6{hpRE6F{AWpl7>VY{1$AdpiPB=76>7kWgSy^ zw}mBGkUYbuQhk}m&ov77(+u~92t#7iz~MtpuKzH(p7^P>(a)9eb4@n6&NR7hDbH2s z=fby|S!2rN`eu2qn4jw=lj}H>>q46gh92mON0qwbdriLMkvcq zS4HY@?jxvr5`(LyEXH$Ja>cnTxnh73au)X@Uk4W)x;7!1)Nmk6GDI%1!2Lx)DBzr+ z1@c9}kc^yYfl&+e2vLr3y+RT}6|Z>iv(Kb0!=Zi7+2?-y%qoblvd<&-dB8riCgN-D zbBJb%=PaKyjTP&0@gk4^h)+1Lg;X?A-lzL7NyEDZY;&YH@FcVR{1gS;d+a(Vb9|TkEQWJSj^OX$PkHwdSHA&=VmmD7Rab# z&%Or6hX!`0q>K-Tv(aKAZ!aJjk&TQGHHPpnejGGPTP(>p#oiMz$;DC*cD3y&Y149) zvL=a0?>(0<#CEz=n5^hs7;nSyauO(<4A0z#>tToXc79Zblfw>mJ3nIYmUJF#hLHx) zU1R9RNY}U5$kZQFAD9DBrtoNX7~wMOFjkL3=}Y1#d7ZU|Kk=rbb9v!*xrz4N${}hU z9!{?4T0_PXj<pKt7L&-QvB3RB zpu+&)u|VDe)`y_yEtd?|hu}GFpGliNv)4Wk+2=m{%qoaqZJ%@Yx!*psCgQ8?^N4*O z;1!37vEc-U&-@;rO2_{B?(gH@Wt#0Tk%t3M##cRvGuh&+?)pK{S5XI#9w~;`|9$9y z6V9iqHwWl98v5%XkDaHVo`C++BK=c?^#AgHkqqU%^Yr^CpkGy_ z|H7sK|Cbv2+o7$Ur=OjG{wi$#82NuSNdN7I{&(IK{oDlfog)4B2I>E9j>zYjH$^`* zWk>tT|JLXK?u`NYoMGrc(*GvpW7Y_#O=C~t>`$!j4eZV2Kl%;woA;0Nn(P4NG-H<|yTH<^FhKi?cbN59GZ=e^1N-+F`m_I+_U z!>auI7_FD&7?3~0DC~=1f?mZx7>T(|Y+vbw?I0GY3p~!oCINAMOr(!5MtK+Sv^&__7^Wh25H&p#tBzpBY9lG&8u53H>J>cV66nL!h?T`q+bv zV@2iQ%3pR5!>{6OM0nZ3j#uf&@i6Sio^!e%52MyfTPvD0ajnU~Qi+J*-&6l~TYu<( zsD7+w1=_#jvCANN%fgg5$HKqX{_*vX(YTBXs{WtU&rV?K-|c^*{_*zrsI5Hm<|>B; z{%i6dU;iz({>lH3>fdJTpY;Ez{#R}N6>q(Md?$pT4k>p$7^vxZrYzgR@c$}?HwC_D z>Q2?2TH=l_0rAOP^kbmU5HVBp^~1uUb#!m}L$JH*AD)7Jl;!_&G5@8-{1;>D{d)O7 zR?L5PG5_1j^TYmd@T4-1su3p4!4=?7wzL@_ZusT70+urNwh=rqX6-&6Bu1ngr#k9x-4gMz3{Qj`B z0C|~z+1Lb|Dfu>VJATSF%zcwO_MK~QHZ{q65(ghFq36%OUKTRb-izNW8aJQon1J|V z5|{bhaY5pLm@ULlDkpCI`Rmf}n}Gf(w>*UY6Nh%l zE#nK8plxi2{h$RM?gj*SSdm%K?%s+`mLo7_e)Oy8{D=~3G57M27t3R{-2B5r>Hf$v z56ez5#BxcPXB@t(+1={6%R1b<9rv`Q?qr69`lx7-!%f3g%ML{pJ2{0WpqB?m+C9*~mu-G6@t8o<`Wx*-9_xQEzW$&>$ z{&}p$T?Q)u%|dA8=Sy3TOHn?=_j0D6EffP=j;0;Oy+822lu>U7W7;J7aJY<|TuZk& z{@z%8;t`$yci>7k8{dzvmH*{xf3q=3;!sra#XQ`_SPfJb$$!mo9)o(& zkDxCkP*ssX1`sF?u{-1C%$(2HiFtm><`ebbDvKVgkyO8V-NoQaC|nVsAb!e=3`!RF zj}zdivfI*(To5SfrAHwM)?SS)BWN=vO@7g=YOD})2UkLGtoQQX?oIQRPZ(z%AF1g_ zXo#Rv)bt-Dp{VI~c-%qLKl#l#O%GUeo>0>l3&nq@re|9ot)}v0>cF%(?E}qUi6~Y&g5)8a>~El%Sq}5G*g} z0%%D0+H#Q}24Jc9+dlHnFPB&f=utYW@h|Xq&tp|=nvHCB$9 zMX$ge*-5XEBO9QZ!_T9`9N={oq(L)p<1{L=vY<`{-; zf-7k5UZesPy^xy~roWDdb&CKP!$r}gf5c2=^9NY2-T8nyVPN8E3@@_I8Z^I}hxElv zZ|AEPiy@y-=Yi~!?L=a-cF&5tM;ArEFbVK*J^pY9Eh#meS60JPQtChrAGI|sH8m{f zK1Z5#P|0$s1n(nSdqzF*oc4GS>bc0XL&L;FTwXfJdyJH zsz9-Bu2UIZ`7s*<3I|oM!@=-&P03`$j;ZW;k52;WM?b-aFLx;3_BGQu~WYPRHT0cgvN8lrH`C)@98Ac z=c^d`Zv^Q-e}FXidvFIkH1~Dr9|QUmHurV%AMi%`;aTRztxTc2=J>lY#bxh;&G{?4 z@}5Q`|1N_C(_zXP4X!TpO%P|rpM|Y-u-wK z&SCeRTHo2Z`mjtMavJ$}hvs4lU7E`rrDgPnon6qD{>=3nl!wD$$HsaNX4UB0?XZA# zAvjT(fry_loN=b+k0!%8$iUZ7#-RfSJ*5m?3unmX{tz?6aY(1&(hzS-U4;BV_Bj7A zJ=yy*LPLg!=KHr^WC|#B+j zEUGSqT}oqlI;;D!DZdWi{dh0FINW;|8npm9s`09OJ$m9JZxew(w^Dc#X~0ugUyi&} zudL$7jC#$*OoX8aw4^3$N*#z&>#8AHY(KNVfnV~uVk6JbBipY`w#U)nbx08jvehoi zvD40!Q;stcJXI9xz|05xnLhmm6vL@^&y?Q32c-|8`Z5dS3n(WUOz{E@?~Y}YG4u$6nBli%_l?J}P+lc#))VT2m&zy;jIuU9OGkqIQI2$25d_t zmf6=bbJkrW@xrP5iX!*^0^)@|A?JzX1+-sE!!}GjbG%d}TaLAms*hLuTbg*q@Ib@` zbd+A>kB}L2kAuDswfJA@=ze}I@TCrv2fr}&{Bh&~ETSe#8u4kB#Hahis`dxV=&85V z3d`9Q=xfI#yXj`o3Ju+88T6nG)=mZG{SXt65`D7A;WgWf)A4P z;%f5QZ>VCvAqzWmU$|f{xy6vc-?9gme(!O{DQLx$kM?%3?+81Xa1N+LsitQ|V5hgG^*o8hN z6QQG4FpPHZyNMtzgklo`j%%9qC}S^>sJN8vw#!ghXaTC?-aWXlGfc9vULm;MD0HJ$Qos$shGc$w z1$bb8_n&*ppBGfw1t5zTpV3|@2cOR8H}tu(B<;k_yAe|OJ9V%%j@OyU)8B*|0~XDA-rYeEq=)DP`N|jn2Fis^5iz75G$E7y5N&+)GMA zCBMDeEa}3KLBjN9iH@o$$_D>y?bUpOP0)@+F>^O{ny)+H2~(#*d|{}{zV3%c zhwxgIG7ZUmCd;^Yq(Nr}~oJ4UelHrfJY(t{fJE5XFzS!8_iaM5ZOra%l^{GLwSDJNMj1967?Cr_2aAQr@ z$gm)kl8imF>g0H*pao>vYeEeezDh@9d(5$ZuL%v6mmGWiR4GVuIxw4sV6!h=x1@!_dsycDp4jBv=+n|jq~ofvOmqD~!}#n4awLEo=K?{U}!nJ%X% z48txA!z4!!StQ4JGEW^ECyOQre_iV{v{YCJ%0t4m;(p}KFQ&=kfiWYER}v>lKM?24 z0-l6=jEfObtwlz$6x z$oZS%bkLuVj{9DfbbUCz{25>daaPoJf4oc+4fg*U#&`@w@v~3gF+VM}ha7a>gE7_~ zHpVJROXO8&`ZtXOug^oe413%hujs=}Il3}_!8GI2AjCj;ga#l1f-5c%?4bYgzrQB& z5k%mesi@!=Tx} zmYeE_di00H?))H97GS6^?-Vq(1sRV7F%%MWhWt=DgpF1sPNmVG zL=0I^Le`TJRRf3YFmTy;8}df|e#A<^NO3Z+n9`6VxS8twi~K2pfa0xDp?rhmKJU2f zWWv7Ku8ahm_)|zXEwcZd1Baa1gU(Iwv;VjZhor&b3g^JP@a;0Q^jXQf8>99g7d~zb z+kZ~6XbjZOUUcaFk9G=%>zqhucc9;i^TYA1*jQo%X1b@h7QpOHZG`|O2@Eq zf${n7Y2fRO*TeOF{IJ8O2ht43;{&n1JR-YKl+blmiS{q#*u+ zenHdmhZ}2M(6TsV?)k&cmQ!|1?bHD&Ciq-#go!md1Xwf@S~*INI&LK#&WIdCg}qn% zN#ZgN9D&jKzdl-uPkfg-d|Px%rGI<3#Hq2~7B+Ow80gjH4Bh^Y1?e|8oiu3LDb#i#MY9 zbX(l4T-BT6qpUMibG<44MHWA6hvKvyMjL?zEMNZqybRCq8Kv)in&<9gjo-H8U48>h z-;@x1hv$38;iYHe&oI7GROkrR`~&ln*%hOD^wIxYqudb~<&Fq=w-y0if-jbirJRoqHd`L_f~P$0SAb6x4Z}Yh^^2PH1$PFk=MhEw=HF6=sr;PX2gw zeRz(yv9F@T6Z9e%okvDxsc}}}4A@d57JO)mDfs?+-g}X~&=|%k37Q@q6W|!{8hD36 zqVad|yvTSXjEu2Nj2Wdh8}h%yeA$pkylg%ywzxEyK|14 za!Yf0%9?`68m(nk+fu$SQQp!b|FCFdZL6ePh?XF$Dv_1@6eU%aKf*6xQnjxSoG%!~ z9E_je!kgKf#8n)6z%q0P<6h5Q38sDb3NvHIeeg13O?Vh@1&46A5C7DJh7Yoj{Lx`N z*4oDk^SGORoMawf0e`sLr_wwY>?2=uMA?V!<7D&r2m3h1Jg&2kd`S_Re`z22iA_BA z+sA3<@hkRmck}oK`#4=53m4(C&mQLfQ@pP>_h<5cPji1N@AoqIC-8o6bAJ@?XPEo< z@SZ=pT!Rzwyx-s4Q&Ri9-Q1JEeGZU&$g$?~h4FpH_mJ`Z@7VlQ@GF`Z%^AEbK82l& znasHnJK3O9MfD=oReU;?|}(M^7%BQA~vMN1{=$1QEcO z+g`=ZE^h(2wIMZ@PyV&vqfIZ9cCO$B0~z`++~&C7qy6M-5CRMiIquC*d66zk`j`xZ z_7^plmP$h>!QZ1jz|=6SyoT5RJ=(YQd&5piz8ICn2PMQwX>28PXCGxGIdr5wjQ zkzg1w%MxH*xLQgUI`VHv^ns|3e*FOcUiD?5w>Rl=r(WoHy&s|6@t29<)>!J*H*qa@ zYzRNN)cq7&euLB!IB(j8&!x`gg}DtSopJk;Q(h{JCu8S;eFui zFI%O)Gc8O}43E%jW*Q$f(D4DN@YmsQz$FYHk49Msg8ou55EiSJ9aqzT{@1Pt{>9E8 z%GLunF)>pUHVuB|BbZz*3Q#67=vb6|Cu6B_SeDzvTy8f~s4Vqu ziU{T5zwO+~s1pO~KCm-~bCCM1?7HF@J6};4A8eTNa4*Pd4zbRZSO98zw-jH8=}RbI zhd)RU>H7ursrMbM%HGoF@ATU{E$o_C`NNdi(hF4vMbvH#6s;FkYD?&)pj_ zZeePLM#GV1YWDaCsylrT(35qdBhRFiP&Lkh1PkHSODq|o5D&-WTh2}oeaxX7+iL&NO_9?EAuu7QSF+AuqE2v1*38F4zi|c`E*ups zH;&okS@7M$>>vv+aql!`KOtquvCH+tUdq=@$wrOX;EtEF5gl}^)p>rcvFu?qXacOp_Mg!}};M=>LvP)5P7sXp=Q`VIuJSna` zW*X1Hyaq&X6bjffq`&5{G+;?`oLX@kn<4uL^Wuw3HVl1KJN|fRJ9=q#3-;}|pCL6( z27l0Y`2LbXokwn9Oj)2jn1jY91C6Z@wk&B{CIb?NG}{UcXpN_F+U!Ms_92SK4sRT% zH{^K&RKl~Fg(ZGf^7b#r@&6egKqopnBVmq3;r%I&>NJ! z_45PTs}XO&-O?M1OPwwe{+7e z{)5HPt89K|{1?xHRdHVrM$f(ie$ap5E2)+Q|0MZ{rts_$vMiP(WGy@|$vEKHFn>Ra zt^_GE!N=%@eh)`O&!2$9I2I{W#7%t${@^9JF?&L9D!?fSl527L7zZus#|;trz0%6U z(>#-G2?;+PD8Je-KN+Qxh20Rq9&1QEPZD(#kdS#Q+XZ3}Hn_^;%`E<8DQczOGm-p- zG#L&^IQf!>!Q$ly_{_#3rhAbupNi1vNEmYc-GL#)cYSh&JvaO>-Vc5U_WO;ahaXKG zj25DG)0vp5`N}iW$XBq95s3f%@omVuMB7z;bC97|{45UhG0ReC+hg2if{nzYec9ey z(~pOH7si({VL4@!Mug-U1y z!VyfUaEzP1iOp)D6^gH?CEGOG1Ab`@X!L6weEgx_*H^9kGI*;b^Ht7G2<`b+6{IwR z?Rn`DgU`m?7r@huC@gcF%cuW(>-*tPO6P=hDoi&jGvA8B^kUcQe4kmTqOtxCo9JwG z*?#ycCVG)4PKJ;U_brvWX1n-n4ICKFyJKTxm(}0xY{OiIT@FI3<_~)$E~+S8gJv!_ zL*l=ueUtx{_PtcrKHC!?HSLSAeZ`*m*4igS5A03mwTxBrry5tegR)^UkjD=BQ>izX zKk>%-t7WNd)N!%SBR!$Jd_VQA;^6-k60W|y0xmfU?;9<()U@vv*)BydM24BY6(!h@ zcXu#(1d1Fb#6Kq~OQ$qc@UflfQfKb=3nVs&6PB(kBh0PNY^)73;G-gaH_+g?GeiI# zZ~+(5%^y}c#PN`ZqVV7B_3-472K}{NG~%wC?cdCXkc}fQvY|q9hu;T><=DMHIskm1 z>6Im@d-5}AY6P-~_>qaYhdWY`gw0aP0qohp+<-YCFSHqwTEg{BI7bqTFY)jN4ougU zxSN)Qhs=A@@MBZ?fhjK8*Wqi;oGf^en{j41z>cNIhBI`A;Lu3Cb0Og@exz%Jt%WlK zElL2B3ZaGg0_z^*NJ#_0-_kc!R)Up4)?DmGHBcK*aLxT=mwYt{5|ePi|1M4e?9e%j z;L!w#H8S@kHi2iz@MMQp{Jzih)1NG!N8FOXk_(eSQC{@GahPut<9op$U~x2LDLsyn zr%a?ow+m51TJJ?ZE}D}TOVi{}%8xhAl5~D|xbC0^K@Vbl!}WDEnevABhs^(1{(jQ2 zPhd{|UJgpeYM>IlS-B@ti(3>6xeX{50sRCHG-G~xzl>&_x?sSZ;P;b$EaxfgPmE&1 z2zi+!z?>(lzn^sRi5!LO=^pRqcpA06;qND%Dh2*`{(jQ8KP{u@HU}5G%z*wgfo&8W z6>b{DW8o(0-|!bWWTv|7_misp-(WI7u(UJ3pM)-bg!sQIaj2AjjDPC*nBX1Ul^oiF zO^eCHCpy9N7VuAW``x#txc`Ly&G{Fd6_jMc%%cAM1D_z#;LkqM0k}eAdJ~7KxAy)N z>W6QkpOW4S+EM{pA~W8|SI{Bg0I@d6-)oGVI^R5Ue1lhpPmO$xT?Q*Ravl&(VDsuG z-q*m9P+4<9F7vxQX7O&i5XBD$bq5a<8GlRmD#0EK;=(}%f8NyZ@2wH%BTl>g_xH|k zVOwN%+@4{AKG3YP`LGwcs+7%N7xF!PNq-_ks#UB2(x}Sc@*JEVn1y*jAe74M>~Fr& z@9%9vXD^@9&81w35`w?KhxYj6Ri5(_X|CT6ZTv~)kJ>Fb zVwv_zTO|PS-|)m7`$+DF9T~<+%nVC0b_5dOXlV1Bd9>g=1;@iz;Pp+*i^S>GcTFOy-!eaqp6<#dG&j`xwQS0u}$rr+E8LSOH zR*8A&Hy@$ID{(M%;hqd|@R9;p#f+!sQ9&Z<;}QH%M%xfLi9KZ+&jttPmo?h&NIHl6u-_Czk$Wi5)B1w zr+g{ezQLNk#MFB#V|QJN^Ws_=P3UMvak zw|)lsf5-eFU|2mE48dAgF)>rKpJ8k%J~vctiTS?p1S5l|CN6?_a1(gPsZH>eC=I5~y@Oy9Z)RK)m`q^_rWcue zd*a{V<%;-KxLBQ}s1xi|P+ih#ADzn^SRfz|b7qdjy&T)?Bbb>}OhJv{SON~nzX zv?5{nub><#8e`Ef7IR`*kww2eC>z&To zyVE((-07T0>~zklJDoEpuUyeIxcvx6xELl7;>u(&4N20i7t?UtvFbv|!8XH<9s|jM+&){bykQV8+)4#us`Vb~lcl3U5+e#ihS4jX!jMD9#B7)+$Vh1b$CO z#reI+3v36e9CfhJMCWbFbNyhH;;LfdYR2;6ZO+_{7eq`Z4>Ofa!a!7BJ>V;FX$nPyOjJRB`P8DNvk z;}e;y%*0nhGLf5zqMEYkQOq362$|5iw!L3VxW=3_47}SaBQ@{1MSFDud}~iaG6Inl4K~(XX(m z9TEMaGMHXd%xMSHUzH`G=DaVeok#tsB z0?NHl5==QeB7T+w&NcW$Q8ru-u>)Q(5*^sH*Nlb`_lF?%(+bC_IbvE4kR~m@KfDC@ zr};m9a}I~TEojv64fvx`DXdjAKy-|~gTX!ZCe?(cWu3~30{l#|wH{@yU z&Sd)9&SW}&XEL?yOr|(8L7&v?BFtD9h6~e!kBoE{!n76@HwrS5Yk33*Zo_P)*}>{^ zIhRxO-*?dnB7a3{X|Vd9EwJ3*D7GV&E%SL(=9etvk5tB~i$A{{``~mM{$6=t!FrM7 zKGAeq(}$WCH7)!gooSoFXU6xuU%(;*>5_sHdVUD;Om zX*$f0G<~$`jHWZ2K6b`f&gp(Rc44iva&zZFP41Sa?#Cw~rSif9>hG@47S3;Vw>j<~ zTix5oYVJD{wA=>@%aDGd`^WAl_h**qEX;}O$C}df_Q@ur*I)?6?{+k$FWD!Yjb4+* zdI#?-vQ2ns3FH5xCS?-Q)-a@bYo#;y-mc%Zt-QPQ?XBS#>bFK`fnIvvdo8(9XKX{u z+!wkEE#bkE47y)~y@-fH;a2z4t6JT*{;oRr!RFapAnL*V+q-j<@cG8)cnasN$`u&+ z-9@X%a_-RZHz96{1Ow#!P0&g1+UW^srU`~r-R2z%p<)u)>OR-%ZVw3ca-6O6h01e~ zhn&$rwth);Ru8D*h~U`moqM6oI#3EVjhzPH-B~Dn1}PM}<1Jc+L^$RF_^A7l#qs8E zYGH--*v(jKM)Rd{5wWND=th~;#^{Dl4g-Olsi%Xj4z8L)~v>CK5vlq=Cv_t=3 z=V0qv-1}PG?XBKHj`y*uR_{F6=~GQ(fBsb%L+;A!So78s_DXl*>!5Q^INffXm@Le2 zIWkZPt@vpTZ+;fxU_HXfyK~-3}nv-Th*iZ-s+0w3!CG4yK<_;FFGyZ41{OVfVKexDB=~zr3sEzh? zgQ9t;?mDcc`=7CBH;uDxC4S!gC%9>L^XL-*Dx=-s#dH1U`SpJrewIjd%{lwDt^8Z( zXWPR6sK1+j#LX3AOP0LHx3|_z6Xb>+-z`q~g;tys8Qau(0v5ho=MHxs7rkN6p1c3^ z;q)uH3#!wvjNrR?E#3EzwcsbB3%8?SMdA03`zriMtNZL&%~#(?(LUPZ-ZuPgGC?PU z568aHGW#~PX}A|@WGhX!+^4%A-(T2-_e;;)gAQRsYxqgyRQ3pqQ<-1C86IX2@i2pJ z;lY;h^B};aNyKk!3uAQ$-#41OrRzp#Wwvv6Yxw#4yO+qnV*Bd1rswS`e5UdU>CgLsa*1fu*ar0I#|x91+((u) z&3(l5=m6_}k1lWI*kj}E6SI=r#YDGnT@RPHl%^TIVSWgmw&!9zIBpkMJ_!B(02m9a&ufUi89c=)x#?E!n8 zm+m~UX~kF;WV?=qR~u@@lxi?niMlq$LZSBIYk#C~G-B8^;ldHO`WmT+mT6E0@uoPX>ISIp!um^{@1ctQ4?ZSxLg%GuE020FZ zF%M%|*V?g#ldx>n)zmTuySj8}_+AWw(KUDD2OOho7VcfOD%SP$!u8Hw1;;x9ew6*q zaUX<7Y6#dKZ4|FQl&7wz{&>D5)A$6={KyxM$7+r}G!!!KNBlR!nxx}iIs)B|I_{zz zILpIl;u)?y6Zz&;@tK>D1ZY0j1}-v5_k-+@=K{?z{EhbyjZe{IL6c_hX3YwpU(w zDxK{x*MuE30N?BGe54q&y7zWJIi+RpXjc`S>+n0EhbDKh2_;+HSK4OZ+S2{XYf*l- z5YG8-=C~KP^;xW=Joy@_A}Kv@CjBjgJUm=7(}sVWacg`t!Z)~Q!EEq{(b5EeSw^2R zCww0qvxz6+k7tTM=HVIoF>zmy5P2GR3G%V0Vmi+fa$FV^4n)R*W^3RE+eoOfN zma+A1b06p`Aku||qdi}yMAzbn@c8dC=Ro|FkYOg@mJ;x~xa z;q&G#`CsAKfczk(*6^LJv+tVk-UAWCBfjVy_+rc8vsEnzZiUENqrZFsPl?v> zNZZPXqARawlUHtz_TUE+F(-K0S@CifNliELZ)^0pyN=7u!@3Qv=+&kbf6Yz7j^?-8 z!nd;?_x2@yt=`eC-dR;`2y?gnxn=P0Q(6X}o7}eBb8QE1YzuE|bDwP4b}#Bf@-s+& zsqMhq5F1vvxz85U|Bm#3v>o{Sw(t{e;eWPz7uL49+gi5W2SO+rUq+6K&i$FA8clE6c2~>bu;ggn?M{<}bPwDHnpMt$&!Yss z0ks8B3Cu!@uGk++rNbBd``r63IvP$ET^No5@dbh(IT8LU@#6)bS&obETKK_McYTZd#};?nidQ(b z?wo-Mw|g(=UQVC}ATc7psC)S6E0%KlD6?=jUqY4g0 zZN4fl!=eF2F;>$NFAiGx@vi7v>=cFEE$Nr1UT|pihQZ8?bY{lT;M2RGck3tJJCk)A zlhGUStCo$sw_y=rP33|m?)>d?Mx^KNWc137n8Sa4Fxm5R7hVP1f~C|aa9S{WL+gp* z-qy%#h{Bt0B>9%vgRPm$I)vQ+lzD~YZZj|FZ62%H7efR_`|2}-4W#^i`FZ^8+pJda zvZ|)^F`>>wFmT_F0raLy5Jsfl*BZsrDZZ?qH0>RElQ`F#nRbNH{IT(4OHCI)4@X#r%5y^q0G+zi?8? z-={B`H8vF+ZLPCkZ3$yxqivuUFWg?b7ISunJZ3J~=04ZMxp#l7``1#f-uyeFD+l4W zxL$ZYuB~%lxG>u0{sZNQhChS^tUvI+uurKzc%Ig|Pe;FSBw!4lt+Ss$F;jUY)=8qv zjzIZn56?N*Z!N9|q~|53Mte9&V)4S6`+V24!!P5Zh__}IW4!+BXv^&NEx!v{H{Er* zllklj1|QBbTf^G|{`n-YdDdu~JJ?qLSeyHF+w3iEyWM({S2?S#KG)_Bx6K}G+ikGr zzz5q7+|n}lK?I21>#JMlzIf55!u$R4R0g!-cuD>`kCKIz7rS0ywaosd5`XR^t#cos z6gr!uvyvgnKh?J7g+ih&bLjh9#(v)t-r6$v-e@-$9n_hnu3(40 z#|j+}!Qw@jB+fMRKJfIwtN>5!pIk?*^`bXTCCN;r;J}UCx#HzKI{XD^HLX2;=9;^j z+|5S5&F-Bo(O+T?@!Vm}=iXO+5aKlV_W85#nSbs*7tg>bJoxn9P3JxM$)?#4oq)+n zYDu%Zu;yt@NbV^;K7(NFwWbv>U-pfwF>1fO?C0HMyIph`mIgL94dyH6pPMy=@_Wud zZ__86XWtoJgSp4Ne(G!)J5+UG1Y#g=%b4M$zM-x;qv7Ht&mxpcRr%(Ig?x9qIhhWE^6%0O{-
r=aY%O>iA>C-}ZxcXkGbPd;A_Z9Od?iCEN+EUO%E`Uhg9#OW3s5vN?4 zn)l-z31%BtqXLW?V>Jy2gfM^YIvkaM9lDT}B~v+4_idrY!G7=&Hk-c+`I|8J#y3jK z7lokzqJCSwDQlr1RMa>Xqadb7Eh|Pv#h-4u@dKxSvEkm|fBFb4xQvTOTd)>`Wfr`% zGx+qhb8n@>o7~%*X0LBLcRlqqnBV)n`>AdjQjT+*YjT*Ct}i}4-jX@)wg1!JoxsU) z)dwE`%lHuHkT3?!W!g9d$+o1P`v{OXx{!iw2}!nWjG5Q!>5*ERnQnS|B+HoO880j{ z3n(}^fDL|Roj?+jh=c{3^BIE!gbgx;4eSwKIMyb!0f7SqvSjjqRlm{Enzk_upL`a& zK7RA<`d9ty_*d1dsY=B*-Y>cS{-W!o>rel1OnBgQy#`Ob{QB!+aC~K-Sa(XfzO6kF zy;XWa)R)ND_uY7>KDj;T`ANzqmqzp#J#8BLxIXfKT+h-6@7T0l zPt5G)QvZeL8^5YI_WK^Zo>XgZn*nfk4lSu@Q?IwcJpFm@>^Xa$H2Zk{^o-fXd!9La`kwP=Wy`h~Z@qlS z6(d_lUOcjOWZTGg{bMgzjO@^VSoVPnH|lq+qx$9AHTo6X)gvz%xoYIfmyKTYlB=%1 zGCf*u_}s$V+lh86(5XPD0-XwUD$uDwrvjY{bSluPK&Jwo3Un&aslX$zKv?9mBzs0~ zeOd02OOWw@{$4&_cvNn&}0 zeEcX1>#LuZ$Lnv)brpO~u4Q`*2N~ay`}_~&)~9mAhjSPEeEaR&^YJaan7%Lf=(}@^ zC}a8fq1$t<&yD5WqiEQk!u)dIpHDZ`@yL7g@%sC63op-I%63}fS1@ka9(O}NpHQdb z{BjvDT%S)r`r_O`zGJ#${aX&?(<96KE0*J^%b~;hbkFo7qxtxudvh(CQa*l^^Tx9O z^;^ilC%1TaZumHHwrlC{QD=t5Np8buu_;5~b(pWmVd^ed zGCzi^^6@(39?eVgxG?=N7Lv=8ut5WBaatfoh#G15$$g=o$;glnT|(@8%O*Vxkor3>x>_|E{_)& zcl+}3rN5ag2ax&ftMc(8<44i4J^E+SBn^jg{yIF$dgV3w^aA@;d}BU-A>}~npBYGt|9*i~EAsO-|s(#=m> zUY#1NT)VQ|C^ak7E335?T|>P?&%Om%VX=MU6sOkX=b9C&Ww}KjmovY+ zk1On)X%;F|WBKR;=ZhZa^4{vyuC$b&MZG3!yR=xnD|@=LVvUa08`XV!o{X02)wGU6 zzKu2AtlD^CXKhBeZMe|fT}fpsRHqB!oNSv@B%d|K*=gL zt59xqrw@zu%pR*`C-P&Cl}M}isKT-f!(*1Gs*rNhHI*)qa($+nT{)**RnrHi%BxDv z+GI7mhR&Lttk$wkIq|Ygi`cchyff|P(ne`Y51kgCoe0k^u*>t|ly3pPG^D{TQ^nXrAT8oyJd`j)o#keZl_Mg>?PrW2dV`F*8vS>Ky ze{$BAk;^u&N!QFNo0B@o)b}+<^;6oSeAreg&orvd1Dp3%8sihS{b#h?eb!d@X@6Lo z+E;Bf$M=tN+=qwr9y-mOerwzDp&014$ zD$~{LD(SLLk-SGrU7hBqD>0QrYj|B{c=*zhEA825Z{Mw#wqE0D7j=~-z3zAEc)spN zZC9f-xw4SWbh+Md#`Uf@R#;uxUDBOs6w13R<-MuDsAZjCiESyDrqU{PdmELBswQZ; zvujPO(LAv7rz^|or1n<7=%r~rvyXM2XEsXLYG-#(L8Q7=&MGu^o;+GUVN}j+jGZv5 zGf!7f${jmlG|gN;Y3g9%q|u=hMs?<{;z?7xx=tSLK0cae?m0fHo6*&K@@U`jQ8}~R zIBCh{#!0#5#tFId2rldyeSDf4OWnY+TFRR)nwxu%wtKZjZ9md>?!|L+E46LVwo}_7 zja{$aqV2G@&uII)wiN$Y^}I{w=DM|Q(pJ`XowhV>q4wK4?cLfwrftjC&D+*%GM*RH#CWYhZ1TQAi~+ecP!TDS4C zZQHkAp69M#w|&iq6PER&byscMFtT>@j?rz~N7lS(gD!ZBKDTVVZ2jhf4m(D+UbgLp zS8ly@%UXSE-E~=Q>eABm-qOyA%Er{YG-m3})s0GNZ`RwaPR|2d)4g-!ly;8QvCZ{# zJ6~Jbr;k;a)tV#wO4W&!yS6ryEg_p}`}Fj-%C5=ER8wc!qTQE%+SV+U_r9<)RcVwa zwlr$ZN;&m`BdHThGp}6PC2$ zK)uq?dqS z;Ba9ob&uhRg`EddSD1RtY--{1i?i$Qba_(tRHTpnJZK zS{v4$FfDMpkY$`Ei`svXC#0;Y`uHPC%I;S4k1_d^TjA>BC55hQ+D#c~JEeQDt9PJx zXrO*D?=4JMntI#5 zV0>~5?N^!m#9lgzn7Z+ex| zwBCGE#!q*%`-Q*J?X4VGk)L4GGxhAHLd##dRxjsj>KW2e)<>M|`08UsWh!+yV}%)g z8{U;2Tf29kEsR%6=@qa#J<4;@Xj8Ai>~nrJoBBLcukEiiR@BB`g%~)_o#_d zy`CYOJRXSiU~E753ee0cbI+h4G3dEp}c0%K;XsV|DW zil#Rs?R~E6^ylfS=)BXV=h&a{b@ylVjH%Bqvu~N3`=Pdr-a0oosI98)z1o7d&uNpl z>Aw(@+O2bQ6WZRT?JL@za@*Y8=Jo5>ZQZ(g>)LgzFMr|ots`sJJw%W8|JL(8#Db4? zl|Sv{)I%)$*hY`*>xn)V6l#qzeVj;N`#${R-LbB_HThv58M0m>eYcc-8B!S=)yLBU z-+;{b9{(;KqR7>^&&}=9_7-h-X#1kJ=X!m=)%JF6U)1)vcm5pjfe%snpU=?}dEf8P z%{^1wow)Z8=jLwK_Pg5B=S$9Bw(RWXm(0J2Y|g&8YW-@kVD$~T!dHXoJGNu@N@W=KMYNg!%M|G=E_^;UQpBUr?)c{XakYJze&ousD0O z)Mw!Qv7uNjb``seJ;mN)U$MV9P#i1{brrk1y1Ki1x_Z0%y862Yx(2(3x{KXi-QC?i z-M!s?-TmDI-GkjjJ;k1`p6;HWp5C6mp8lSJp241>-ePZ8Z+CA`Z*Om3Z@<3u4fYQ8 z75lpSy8C+idi(nN`uhg@2K$Eki~U{w-TgiNz5RXt{rv;|gZ)DT#euGY?tz|x-hn=S zRU8-?92gob4t5Q85B3c94)zW94-O0t4h{|JCJyQ9^?yU@f`)Xap`lWaFfS8no!pW59@VChJFo79DyF7*Wn59}QY`=rLeKSxi0}wCK=dz=-l_^3kA0haLk) zlQFQF{Zt~HfMTZ^(MwA}%(V#_#9s@>{Uh>hPMTZ^(MwCAC(V#_#9s@>{e)7?v zMTZ^(Mw9{a(V#_#9s@>{LGsa{Mg67gI@~WF14fh~^3kA0haLk)lwtDGphbrs14fiz zBOeV~bm%c)M0p&||=e^6TWIL5mJO28<|% zd^Bj$p~rv`Wkht)`#&1A=+I-ph_ae|G-%PG$AA%K4f$x$qC<}XBg$Ix(V#_#9s@>{ zb>yQ#iw->oj412LM}rm}dJGs*UPwL~wCK=dz=*Ped^Bj$p~rv`Wh41$(4s?+0VB$9 zkdFo}I`kMYqP&QFG-%PG$AA%K6ZvS+qC<}XBg&=Zqd|)fJqC;@mywSKEjsiVFrsWG z9}QY`=rLeK*+M=VwCK=dz=-l<^3kA0haLk)l&$2WL5mJO28<}%$VY<~9eNBHQMQwh z1}!@D7%-w-PCgp6=+I-ph;jw_XwafVj{zgf4)W2UMTZ^(MwBbbM}rm}dJGs*t|A`| zT6E|!U_^Nd`DoChLyrL?%GKnfL5mJO28<{#B_9o1bm%c)M7f51G-%PG$AA&#W#pql ziw->oj3}ezqd|)fJqC;@mV7j5(V@qH5v3$L==~oJT6E|!U_{wTJ{q*>&||=eQYIe_ zT6E|!U_==s9}QY`=rLeKsgREbEjsiVFrti;j|MF|^cXOr>>?ixT6E|!U_{wXJ{q*> z&||=eQY9Y^T6E|!U_|`|)H>+-g9a@+^cXOr>?I!!T6E|!U__Z99}QY`=rLeKnIs<# zT6E|!U__ZB9}QY`=rLeKsgaKcEjsiVFrw7SM}rm}dJGs*UQRw5wCK=dz=+Zy9}QY` z=rLeKnI<0%T6E|!U_@z>j|MF|^cXOr%#e=;EjsiVFrw@u9}QY`=rLeK*-t(iwCK=d zz=(1!`DoChLyrL?$^r7xphbrs14fkV$VY<~9eNBHQC>kl8no!pW59@VJ^5(RqC<}X zBg!kuM}rm}dJGs*ev^DOXwjj^fDz@l$VY<~9eNBHQ5^Yb(4s?+0VB!{qJ#F|nEyqB z`z8Mi1o!g`+4TD(rU#5DuOc4}T6E|!U_`l*d^Bj$p~rv`Z;vA|DM} zbm%c)M0p+gXwafVj{zgfegBBfn3>Z<~NIn|0 z=+I-ph;oR0G-%PG$AA-C?79DyF7*XCtJ{q*>&||=eax?j8(4s?+0VB%qkdFo} zI`kMYqP&@WG-%PG$AAV^=cMTZ^(MwH(r9}QY`=rLeKc?9qC<}XBg$>$qd|)fJqC;@ZzmrOT6E|!U_^Nb`DoCh zLyrL?%I)N%L5mJO28<}4d^Bj$p~rv`<(;B~-v7~{MTZ^(MwH(p9}QY`=rLeK`OoB| zL5mJO28<|okdFo}I`kMYqWnJjXwafVj{zgfACQj*EjsiVFrvJRd^Bj$p~rv`<=y0? zL5mJO28<|wNIn|0=+I-pi1J6|qd|)fJqC;@e@s3awCK=dz=-l5^3kA0haLk)ls_RK z4O(>QFQF5j>gBBfn3>Z;9Pd*y7=+I-pi1G#U(V#_#9s@>{zabwDT6E|!U_|*M z`DoChLyrL?%6}st4O(>QF{|4BX?wCK=dz=-m{$VY<~9eNBHQT~p6G-%PG$AA&# zE99d=iw->oj3|+OG-%PG$AA&#??nf_|D!>R4m}2pC|@NX4O(>QF(V@qH5#_t&qd|)f zJqC;@50Z}tEjsiVFrxeu`DoChLyrL?%J;}egBBfn3>Z{eZ=Vg?uz<(V@qH5#|4qj|MF| z^cXOr{Fr<+Xwjj^fDz?i$wz}09eNBHQRc`;gBBfn3>Y(0ZW@;6$YPX(F8fq?ZSGNA zeKa3`QZ`lOS?c-PTH_aJ>q5X zIfdr8d)`CO$meJFQ>L4#eEOAK2Tx|kC#vUYYmFB)miDbR{wo@LMw@u5-9q#CEf9Z> z#^#TWew4-R-2DC=UFK3)>?OHJ@ab%SAx+fbxtfrUO>6vV8c)lAUCVVprLl$LFV}e5 z-`4q4Je_l`@qHRg=WA>H`Zn=5wu!&0P5fY+_?z3rZ`F9Jf9v+#)+YY;Ht~0~iGN7r z3)StT8egbxDZWtMQaqhMt=su=jV)Bqzibo#Y@7If8egampKlZYLYw&Cw27zlKb@+r z+ix_s(0;9N6JOINp8DZ*KDRD^Q=9muZQ@%rp8DU`>&s<>sbRMN=j@IRWQDf)uuK6 zO^u~zlZE0JXuNfx`;|EAPMXxNj)m{#^QGgLhQ)g0O*Q=_^?g&yY|e7_QzTs%sV;|D{`|JSOi!NFKNJ_;kj;5+_Vf5XaV_z5Jkv1$9I%aeT4oyb zHku7DZJB?5Klc((eMlPSp98KZp00&76tbTpHxW;LU>Z#JQ`gUZeSUq8_$|!e`W*1q z);P}rE54Iwj*>s4raH9Nb4{B%ucgkd$Ll^VP`VylM4w; zW1NO_TF%F_TgIQ!GXDiF^QHq^F5HW$6vF z=iT@~;k9>vD4jN6_}ZPKzp3D^bUbeiryo2|e|+`(_vn<) o(5XPD0-XwUD$uDwrvjY{bSluPK&Jwo3Un&asX(U!4_kqM1G4?d%K!iX literal 0 HcmV?d00001 diff --git a/tests/data/igvm/qemuinit.igvm b/tests/data/igvm/qemuinit.igvm new file mode 100644 index 0000000000000000000000000000000000000000..08e605ae0d8164b115bd2ac8b65530ecc7d78eaf GIT binary patch literal 137112 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 arvjY{bSluPK&Jwo3Un&aslWqP;Qs Date: Mon, 17 Aug 2026 19:50:05 +0530 Subject: [PATCH 664/860] Add functional and unit tests for the vm-launch-update device This patchset adds functional and unit tests that exercize various functions and behaviors of vm-launch-update device. It uses the IGVM files that were introduced in the previous patch for exercizing the hypervisor interface. CC: Alex Graf CC: Gerd Hoffman Reviewed-by: Alexander Graf Signed-off-by: Ani Sinha Message-ID: <20260817142010.80693-11-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- tests/functional/aarch64/meson.build | 4 + .../aarch64/test_vm_launch_update_aarch.py | 33 + tests/functional/x86_64/meson.build | 4 + .../x86_64/test_vm_launch_update.py | 48 ++ tests/qtest/launchupdate-test.c | 625 ++++++++++++++++++ tests/qtest/meson.build | 2 + 6 files changed, 716 insertions(+) create mode 100755 tests/functional/aarch64/test_vm_launch_update_aarch.py create mode 100755 tests/functional/x86_64/test_vm_launch_update.py create mode 100644 tests/qtest/launchupdate-test.c diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build index e81afd6c39..f0881bed16 100644 --- a/tests/functional/aarch64/meson.build +++ b/tests/functional/aarch64/meson.build @@ -25,6 +25,10 @@ tests_aarch64_system_quick = [ 'vmstate', ] +if igvm.found() + tests_aarch64_system_quick += [ 'vm_launch_update_aarch' ] +endif + tests_aarch64_system_thorough = [ 'aspeed_ast2700a1', 'aspeed_ast2700a2', diff --git a/tests/functional/aarch64/test_vm_launch_update_aarch.py b/tests/functional/aarch64/test_vm_launch_update_aarch.py new file mode 100755 index 0000000000..2b3d7cf4a3 --- /dev/null +++ b/tests/functional/aarch64/test_vm_launch_update_aarch.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# +# Check for vm-launch-update device. +# +# Copyright (c) 2026 Red Hat, Inc. +# +# Author: +# Ani Sinha +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest + +class VmLaunchUpdateDeviceCheck(QemuSystemTest): + + def aarch64_fail_test(self): + """ + Currently the device is only supported for pc platforms. + """ + self.vm.add_args('-machine', 'virt', '-device', + 'vm-launch-update,id=fwupd1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), + r'This machine does not support vm-launch-update device') + + def test_vm_launch_update(self): + self.aarch64_fail_test() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index 27b31f2e96..0353b2af8e 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -28,6 +28,10 @@ if not get_option('asan') tests_x86_64_system_quick += [ 'memlock' ] endif +if igvm.found() + tests_x86_64_system_quick += [ 'vm_launch_update' ] +endif + tests_x86_64_system_thorough = [ 'acpi_bits', 'hotplug_blk', diff --git a/tests/functional/x86_64/test_vm_launch_update.py b/tests/functional/x86_64/test_vm_launch_update.py new file mode 100755 index 0000000000..aac7ef915f --- /dev/null +++ b/tests/functional/x86_64/test_vm_launch_update.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Check for vm-launch-update device. +# +# Copyright (c) 2026 Red Hat, Inc. +# +# Author: +# Ani Sinha +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest +import time + +class VmLaunchUpdateDeviceCheck(QemuSystemTest): + DELAY_BOOT_SEQUENCE = 1 + + def vm_launch_update_pass(self): + """ + Basic test to make sure vm-launch-update device can be instantiated. + """ + self.vm.add_args('-device', 'vm-launch-update,id=fwupd1') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + time.sleep(self.DELAY_BOOT_SEQUENCE) + self.vm.shutdown() + self.assertEqual(self.vm.exitcode(), 0, "QEMU exit code should be 0") + + def multiple_device_fail(self): + """ + Only one vm-launch-update device can be instantiated. Ensure failure if + user tries to create more than one device. + """ + self.vm.add_args('-device', 'vm-launch-update,id=fw1', + '-device', 'vm-launch-update,id=fw2') + self.vm.set_qmp_monitor(enabled=False) + self.vm.launch() + self.vm.wait() + self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") + self.assertRegex(self.vm.get_log(), + r'at most one vm-launch-update device is permitted') + + def test_vm_launch_update(self): + self.vm_launch_update_pass() + self.multiple_device_fail() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/qtest/launchupdate-test.c b/tests/qtest/launchupdate-test.c new file mode 100644 index 0000000000..225c843df5 --- /dev/null +++ b/tests/qtest/launchupdate-test.c @@ -0,0 +1,625 @@ +/* + * vmlaunchupdate device fwcfg test. + * + * Copyright (c) 2026 Red Hat, Inc. + * + * Author: + * Ani Sinha + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqos/libqos-pc.h" +#include "libqtest.h" +#include "standard-headers/linux/qemu_fw_cfg.h" +#include "libqos/fw_cfg.h" +#include "qemu/bswap.h" +#include "hw/misc/vmlaunchupdate.h" + +#define WAIT_SEC 10 +static bool debug; +static bool trace; +static bool confidential; + +static void test_vm_launch_update_capability(void) +{ + QFWCFG *fw_cfg; + QTestState *s; + VMLaunchUpdate launch_update; + size_t filesize; + uint64_t capabilities; + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + s = qtest_init("-device vm-launch-update"); + fw_cfg = pc_fw_cfg_init(s); + + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + capabilities = le64_to_cpu(launch_update.capabilities); + g_assert_cmpint(capabilities, ==, VM_LAUNCHUPDATE_FORMAT_IGVM); + pc_fw_cfg_uninit(fw_cfg); + qtest_quit(s); +} + + +static void test_vm_launch_update_disable(void) +{ + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + uint64_t control; + size_t filesize; + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + /* use default accelerator */ + qs = qtest_pc_boot("-device vm-launch-update"); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + control = le64_to_cpu(launch_update.control); + g_assert_cmpint(VM_LAUNCHUPDATE_CTL_DISABLE & control, ==, 0); + + /* disable the device */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.control |= VM_LAUNCHUPDATE_CTL_DISABLE; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + /* try to clear the dsable flag */ + memset(&launch_update, 0, sizeof(launch_update)); + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + /* check if the device is still disabled */ + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + control = le64_to_cpu(launch_update.control); + g_assert_cmpint(VM_LAUNCHUPDATE_CTL_DISABLE & control, ==, 1); + + pc_fw_cfg_uninit(fw_cfg); + qtest_shutdown(qs); +} + +static void check_error(void) +{ + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + uint16_t status; + size_t filesize; + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + /* guest not started with IGVM and with default accelerator */ + qs = qtest_pc_boot("-device vm-launch-update"); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = 50; + launch_update.fw_image_addr = cpu_to_le64(0xdeadbeef); + launch_update.control |= VM_LAUNCHUPDATE_FORMAT_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + memset(&launch_update, 0, sizeof(launch_update)); + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + status = le64_to_cpu(launch_update.status); + /* should fail with NOT_IGVM_INIT */ + g_assert_cmpint(status, ==, VM_LAUNCHUPDATE_NOT_IGVM_INIT); + + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = 50; + launch_update.fw_image_addr = cpu_to_le64(0xdeadbeef); + /* control set to 0, not VM_LAUNCHUPDATE_FORMAT_IGVM */ + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + memset(&launch_update, 0, sizeof(launch_update)); + filesize = qfw_cfg_get_file(fw_cfg, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + status = le64_to_cpu(launch_update.status); + /* should fail with LOAD_FAIL since it was not IGVM format */ + g_assert_cmpint(status, ==, VM_LAUNCHUPDATE_LOAD_FAIL); +} + +static int64_t get_image_size(const char *filename) +{ + int fd; + int64_t size; + fd = open(filename, O_RDONLY | O_BINARY); + g_assert_true(fd > 0); + size = lseek(fd, 0, SEEK_END); + close(fd); + return size; +} + +static ssize_t load_image(const char *igvm_f, void **addr, size_t *size) +{ + ssize_t actsize = 0, l = 0; + int f_igvm_f; + size_t l_size; + + f_igvm_f = open(igvm_f, O_RDONLY | O_BINARY); + g_assert_true(f_igvm_f); + l_size = get_image_size(igvm_f); + g_assert_true(l_size > 0); + *addr = g_malloc0(l_size); + g_assert_true(*addr); + + while (l < l_size) { + actsize = read(f_igvm_f, *addr + l, 1); + if (actsize < 0) { + break; + } + l += actsize; + } + + close(f_igvm_f); + *size = l_size; + return actsize < 0 ? -1 : l; +} + +static guint32 match_string(char *serial_f, const char *exp_out) +{ + GError *error = NULL; + g_autofree gchar *f_contents = NULL; + g_autofree GRegex *regex = NULL; + g_autofree GMatchInfo *match_info = NULL; + gsize len; + guint32 count = 0; + gboolean ret; + + ret = g_file_get_contents(serial_f, &f_contents, &len, &error); + g_assert(ret); + g_assert_no_error(error); + + regex = g_regex_new(exp_out, G_REGEX_CASELESS, 0, &error); + g_assert_no_error(error); + + ret = g_regex_match_full(regex, f_contents, -1, 0, 0, &match_info, &error); + g_assert_no_error(error); + + while (g_match_info_matches(match_info)) { + gchar *word = g_match_info_fetch(match_info, 0); + g_free(word); + g_match_info_next(match_info, &error); + count++; + } + g_regex_unref(regex); + return count; +} + +static int wait_for_match(char *serial_f, + const char *exp_out, int64_t timeout_s, + guint32 count) +{ + time_t start, delta; + int ret = -1; + + start = time(NULL); + while (1) { + if (match_string(serial_f, exp_out) == count) { + ret = 0; + break; + } + + delta = time(NULL) - start; + if (delta >= timeout_s) { + fprintf(stderr, "timed out waiting to read serial output\n"); + break; + } + + /* wait 20 ms before trying again */ + if (false) { + fprintf(stderr, + "sleeping 20 ms before checking serial output again.\n"); + } + g_usleep(20000); + } + return ret; +} + +static void set_test_params(const char **igvm_f, const char **igvm_init, + const char **snp, const char **cgs) +{ + if (confidential) { + *snp = "-object \'{\"qom-type\":\"sev-snp-guest\",\"id\":\"lsec0\"," + "\"cbitpos\":51,\"reduced-phys-bits\":1,\"policy\":196608}\'"; + *cgs = "confidential-guest-support=lsec0"; + /* + * The following two IGVM files can be built from the source + * present in https://gitlab.com/anisinha/virt-firmware-rs . + * Typing 'make' from the top of this repository will build the + * IGVM files for both confidential and + * non-confidential tests. The IGVM files for the non-coco + * case has been checked-in into the QEMU repository for + * convenience and easy CI pipeline testing. + */ + *igvm_f = "tests/data/igvm/snptest.igvm"; /* prints 'hello world' */ + *igvm_init = "tests/data/igvm/snptest-nohello.igvm"; + } else { + *igvm_f = "tests/data/igvm/hello.igvm"; + *igvm_init = "tests/data/igvm/qemuinit.igvm"; + *snp = ""; + *cgs = ""; + } + + return; +} + +static void set_expected_out(const char **exp_out, const char **exp_out2, + const char **exp_out3) +{ + *exp_out = "Hello world!"; + *exp_out2 = "Test succeeded!"; + *exp_out3 = "boot process complete with initial igvm"; + + return; +} + +static QOSState *set_qemu_args(const char *cgs, const char *tp, char *serialf, + const char *igvm_init, const char *snp) +{ + QOSState *qs; + + if (tp) { + qs = qtest_pc_boot("-machine q35,igvm-cfg=igvm0,%s -m 1G -accel kvm " + "-device vm-launch-update %s " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 " + "-object igvm-cfg,id=igvm0,file=%s %s", + cgs, tp, serialf, igvm_init, snp); + } else { + qs = qtest_pc_boot("-machine q35,igvm-cfg=igvm0,%s -m 1G -accel kvm " + "-device vm-launch-update " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 " + "-object igvm-cfg,id=igvm0,file=%s %s", + cgs, serialf, igvm_init, snp); + } + + return qs; +} + +static void test_load_igvm(void) +{ + const char *igvm_f; + const char *igvm_init; + int ser_fd; + g_autofree void *igvm_blob = NULL; + g_autofree char *serialtmp = NULL; + const char *exp_out; + const char *exp_out2; + const char *exp_out3; + const char *tracepoints = "--trace memory_region_finalize " + "--trace qigvm_cleanup_memory -D /tmp/qemu-debug.log "; + const char *snp, *cgs; + uint64_t gaddr; + size_t igvm_sz; + size_t filesize; + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + + if (!trace) { + tracepoints = ""; + } + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + if (!qtest_has_accel("kvm")) { + g_test_skip("No KVM accelerator available"); + return; + } + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + set_test_params(&igvm_f, &igvm_init, &snp, &cgs); + set_expected_out(&exp_out, &exp_out2, &exp_out3); + + if (!g_file_test(igvm_f, G_FILE_TEST_EXISTS) || + !g_file_test(igvm_init, G_FILE_TEST_EXISTS)) { + g_test_skip("igvm file bundle(s) does not exist!"); + return; + } + + ser_fd = g_file_open_tmp("launchupdate-qtest-serial-sXXXXXX", + &serialtmp, NULL); + g_assert_true(ser_fd != -1); + + if (debug) { + fprintf(stderr, "serial console file is %s\n", serialtmp); + } + + qs = set_qemu_args(cgs, tracepoints, serialtmp, igvm_init, snp); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + if (debug) { + fprintf(stderr, "target endianness: %s\n", + qtest_big_endian(qs->qts) ? "big" : "little"); + } + + /* exp_out3 should be printed once from initial boot */ + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "initially booted with host igvm\n"); + } + + g_assert_true(load_image(igvm_f, &igvm_blob, &igvm_sz) == igvm_sz); + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, igvm_sz); + + if (debug) { + fprintf(stderr, "guest paddr: %"PRIx64 " igvm size: %lu\n", + gaddr, igvm_sz); + } + + if (debug) { + fprintf(stderr, "writing igvm file into the guest memory\n"); + } + + qtest_bufwrite(qs->qts, gaddr, igvm_blob, igvm_sz); + + if (debug) { + fprintf(stderr, + "tell hypervisor where igvm is loaded in guest memory\n"); + } + + /* now tell hypervisor where we loaded the bios */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = cpu_to_le64(igvm_sz); + launch_update.fw_image_addr = cpu_to_le64(gaddr); + launch_update.control |= VM_LAUNCHUPDATE_FORMAT_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + if (debug) { + fprintf(stderr, "resetting the virtual machine now\n"); + } + + qtest_system_reset(qs->qts); + + /* expected string should be printed on the console */ + g_assert_true(wait_for_match(serialtmp, exp_out, WAIT_SEC, 1) == 0); + g_assert_true(wait_for_match(serialtmp, exp_out2, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "hello world found on console\n"); + } + + /* check if VM_LAUNCHUPDATE_CTL_HOST_IGVM function works */ + + /* set only VM_LAUNCHUPDATE_CTL_HOST_IGVM control without IGVM bundle */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.control |= VM_LAUNCHUPDATE_CTL_HOST_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + /* now reset the guest */ + if (debug) { + fprintf(stderr, + "resetting again in order to restore host provided IGVM\n"); + } + qtest_system_reset(qs->qts); + + /* + * exp_out3 should be printed twice, once from initial boot, + * once from restoring host igvm. + */ + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 2) == 0); + + if (debug) { + fprintf(stderr, "booted with host igvm again\n"); + } + + close(ser_fd); + guest_free(&qs->alloc, gaddr); + pc_fw_cfg_uninit(fw_cfg); + /* qtest_quit() kils QEMU, first by sending SIGTERM, then SIGKILL */ + qtest_quit(qs->qts); +} + +static void test_set_ctrl_once_and_reset_to_host_igvm(void) +{ + const char *igvm_f; + const char *igvm_init; + int ser_fd; + g_autofree void *igvm_blob = NULL; + g_autofree char *serialtmp = NULL; + const char *exp_out; + const char *exp_out2; + const char *exp_out3; + const char *snp, *cgs; + uint64_t gaddr; + size_t igvm_sz; + size_t filesize; + QFWCFG *fw_cfg; + QOSState *qs; + VMLaunchUpdate launch_update; + + if (!qtest_has_machine("q35")) { + g_test_skip("q35 machine not available"); + return; + } + + if (!qtest_has_accel("kvm")) { + g_test_skip("No KVM accelerator available"); + return; + } + + if (!qtest_has_device("vm-launch-update")) { + g_test_skip("Device vm-launch-update is not available"); + return; + } + + set_test_params(&igvm_f, &igvm_init, &snp, &cgs); + set_expected_out(&exp_out, &exp_out2, &exp_out3); + + if (!g_file_test(igvm_f, G_FILE_TEST_EXISTS) || + !g_file_test(igvm_init, G_FILE_TEST_EXISTS)) { + g_test_skip("igvm file bundle(s) does not exist!"); + return; + } + + ser_fd = g_file_open_tmp("launchupdate-qtest-serial-sXXXXXX", + &serialtmp, NULL); + g_assert_true(ser_fd != -1); + + if (debug) { + fprintf(stderr, "serial console file is %s\n", serialtmp); + } + + qs = set_qemu_args(cgs, NULL, serialtmp, igvm_init, snp); + + fw_cfg = pc_fw_cfg_init(qs->qts); + + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "initially booted with host igvm\n"); + } + + g_assert_true(load_image(igvm_f, &igvm_blob, &igvm_sz) == igvm_sz); + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, igvm_sz); + + if (debug) { + fprintf(stderr, "guest paddr: %"PRIx64 " igvm size: %lu\n", + gaddr, igvm_sz); + } + + if (debug) { + fprintf(stderr, "writing igvm file into the guest memory\n"); + } + + qtest_bufwrite(qs->qts, gaddr, igvm_blob, igvm_sz); + + if (debug) { + fprintf(stderr, + "tell hypervisor where igvm is loaded in guest memory\n"); + } + + /* now tell hypervisor where we loaded the bios */ + memset(&launch_update, 0, sizeof(launch_update)); + launch_update.fw_image_size = cpu_to_le64(igvm_sz); + launch_update.fw_image_addr = cpu_to_le64(gaddr); + + /* set both host ctrl and format_igvm ctrl once */ + launch_update.control |= VM_LAUNCHUPDATE_FORMAT_IGVM; + launch_update.control |= VM_LAUNCHUPDATE_CTL_HOST_IGVM; + + filesize = qfw_cfg_write_file(fw_cfg, qs, FILE_VMLAUNCHUPDATE, + &launch_update, sizeof(launch_update)); + g_assert_cmpint(filesize, ==, sizeof(launch_update)); + + if (debug) { + fprintf(stderr, "resetting the virtual machine. This should load " + "user provided igvm.\n"); + } + + qtest_system_reset(qs->qts); + + /* expected string should be printed on the console */ + g_assert_true(wait_for_match(serialtmp, exp_out, WAIT_SEC, 1) == 0); + g_assert_true(wait_for_match(serialtmp, exp_out2, WAIT_SEC, 1) == 0); + + if (debug) { + fprintf(stderr, "hello world found on console\n"); + fprintf(stderr, "Now resetting again in order to reset to host igvm\n"); + } + + qtest_system_reset(qs->qts); + + /* + * exp_out3 should be printed twice, once from initial boot, + * once from restoring host igvm. + */ + g_assert_true(wait_for_match(serialtmp, exp_out3, WAIT_SEC, 2) == 0); + + if (debug) { + fprintf(stderr, "booted with host igvm\n"); + } + + close(ser_fd); + guest_free(&qs->alloc, gaddr); + pc_fw_cfg_uninit(fw_cfg); + /* qtest_quit() kils QEMU, first by sending SIGTERM, then SIGKILL */ + qtest_quit(qs->qts); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "x86_64")) { + g_test_skip("vmlaunchupdate tests are only available on x86_64\n"); + return 0; + } + + g_test_add_func("/vm-launch-update/cap", test_vm_launch_update_capability); + g_test_add_func("/vm-launch-update/disabled", + test_vm_launch_update_disable); + + g_test_add_func("/vm-launch-update/errorcheck", check_error); + g_test_add_func("/vm-launch-update/load_igvm", + test_load_igvm); + g_test_add_func("/vm-launch-update/ctrl_set_once", + test_set_ctrl_once_and_reset_to_host_igvm); + + if (getenv("LAUNCHUPDATE_DEBUG")) { + debug = true; + } + if (getenv("LAUNCHUPDATE_TRACE")) { + trace = true; + } + if (getenv("COCO")) { + confidential = true; + } + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index f7c7d06620..12ed368997 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -61,6 +61,8 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_Q35') ? ['e820-test'] : []) + \ (config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \ (config_all_devices.has_key('CONFIG_Q35') ? ['dump-test'] : []) + \ + (igvm.found() and + config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['launchupdate-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \ (config_all_devices.has_key('CONFIG_I440FX') ? ['numa-test'] : []) + \ From e27a2b5f36a0fc913ab9a55a00f77a33ec6cc4f0 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Mon, 17 Aug 2026 19:50:06 +0530 Subject: [PATCH 665/860] Update MAINTAINERS Add maintainers for vm-launch-update device and associated documentation and test code. Reviewed-by: Alexander Graf Signed-off-by: Ani Sinha Message-ID: <20260817142010.80693-12-anisinha@redhat.com> Signed-off-by: Gerd Hoffmann --- MAINTAINERS | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9e0a2a656f..1f9c08d766 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2851,6 +2851,19 @@ F: include/hw/acpi/vmgenid.h F: docs/specs/vmgenid.rst F: tests/qtest/vmgenid-test.c +VM Launch Update +M: Ani Sinha +M: Gerd Hoffman +S: Maintained +F: hw/misc/vmlaunchupdate.c +F: include/hw/misc/vmlaunchupdate.h +F: include/standard-headers/misc/vmlaunchupdate.h +F: docs/specs/vmlaunchupdate.rst +F: tests/functional/aarch64/test_vm_launch_update_aarch.py +F: tests/functional/x86_64/test_vm_launch_update.py +F: tests/qtest/launchupdate-test.c +F: tests/data/igvm/* + LED M: Philippe Mathieu-Daudé S: Maintained From 1bfce83996929033fa14a123967bf40976888661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 666/860] target/arm: Restrict arm_do_plugin_vcpu_discon_cb() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far TCG plugins can only be used when TCG is available. Move the arm_do_plugin_vcpu_discon_cb() call within the 'if tcg_enabled' block and wrap the definition with #ifdef'ry. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-id: 20260814080507.23196-1-philmd@oss.qualcomm.com Signed-off-by: Peter Maydell --- target/arm/helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index af45234ad2..adae2b2b8e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8915,6 +8915,7 @@ static void take_aarch32_exception(CPUARMState *env, int new_mode, } } +#ifdef CONFIG_TCG void arm_do_plugin_vcpu_discon_cb(CPUState *cs, uint64_t from) { switch (cs->exception_index) { @@ -8932,6 +8933,7 @@ void arm_do_plugin_vcpu_discon_cb(CPUState *cs, uint64_t from) qemu_plugin_vcpu_exception_cb(cs, from); } } +#endif static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) { @@ -9677,9 +9679,9 @@ void arm_cpu_do_interrupt(CPUState *cs) if (tcg_enabled()) { cpu_set_interrupt(cs, CPU_INTERRUPT_EXITTB); - } - arm_do_plugin_vcpu_discon_cb(cs, last_pc); + arm_do_plugin_vcpu_discon_cb(cs, last_pc); + } } #endif /* !CONFIG_USER_ONLY */ From d05febdd32cc6d6a0700994d25ae3ffccdc97e76 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 667/860] hw/intc: Fix arm kvm gicv3 selection While 39a8c3941e may have fixed WHPX, it certainly didn't help KVM: $ QTEST_QEMU_BINARY=./qemu-system-aarch64 ./tests/qtest/arm-cpu-features ... qemu-system-aarch64: unknown type 'kvm-arm-gicv3' That patch did remove a test for TARGET_AARCH64, which is fine because it has been a long time since we supported KVM for AArch32. Cc: qemu-stable@nongnu.org Fixes: 39a8c3941e ("hw/intc/arm_gicv3: Fix ARM_GICV3 dependency for KVM / WHPX") Signed-off-by: Richard Henderson Message-id: 20260812200308.289238-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 4373f16d0b..0620b0792a 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -48,7 +48,7 @@ specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_WHPX', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_whpx.c')) specific_ss.add(when: ['CONFIG_HVF', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_hvf.c')) stub_ss.add(files('arm_gicv3_hvf_stub.c')) -specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'CONFIG_ARM_GICV3'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) +specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.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')) From a0c093900d84f6668c27eaeab4ca74935b5857b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 668/860] hw/intc/arm_gicv3: Have GIC kconfig select GICv3 for HVF and WHPX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While trying to fix the GICv3 dependency on KVM and WHPX in commit 39a8c3941eb, we missed the Kconfig ARM_GIC symbol only selects GICv3 for TCG, not HVF and WHPX. Fix that. Cc: qemu-stable@nongnu.org Fixes: 39a8c3941eb ("hw/intc/arm_gicv3: Fix ARM_GICV3 dependency for KVM / WHPX") Signed-off-by: Philippe Mathieu-Daudé Message-id: 20260812220816.94034-1-philmd@oss.qualcomm.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 097de4efc5..05e697967d 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -26,7 +26,7 @@ config APIC config ARM_GIC bool - select ARM_GICV3 if TCG + select ARM_GICV3 if TCG || HVF || WHPX select ARM_GIC_KVM if KVM select MSI_NONBROKEN From 66d0446738c7be74b40746e3eebe5cfbeea56d45 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 669/860] target/arm: Tidy cpu_max_initfn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Once we have eliminated hwaccel_enabled, only tcg and qtest remain. Separate tcg from qtest initialization. Remove an assert on aarch64_enabled that is directly protected by a preceding if. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260812204435.295067-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-max.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index 88b7c04633..0a310d1b15 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -203,29 +203,32 @@ static void cpu_max_initfn(Object *obj) return; } - if (tcg_enabled() || qtest_enabled()) { - aarch64_aa32_a57_init(obj, !aarch64_enabled); - } - - if (!aarch64_enabled) { - aa32_max_features(cpu); -#ifdef CONFIG_USER_ONLY - /* - * Break with true ARMv8 and add back old-style VFP short-vector - * support. Only do this for user-mode, where -cpu max is the default, - * so that older v6 and v7 programs are more likely to work without - * adjustment. - */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); -#endif - } else if (tcg_enabled()) { - assert(aarch64_enabled); + if (tcg_enabled()) { /* * '-cpu max' for TCG: we currently do this as * "A57 with extra things" */ - aarch64_max_tcg_initfn(obj); + aarch64_aa32_a57_init(obj, !aarch64_enabled); + if (!aarch64_enabled) { + aa32_max_features(cpu); +#ifdef CONFIG_USER_ONLY + /* + * Break with true ARMv8 and add back old-style VFP short-vector + * support. Only do this for user-mode, where -cpu max is the default, + * so that older v6 and v7 programs are more likely to work without + * adjustment. + */ + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); +#endif + } else { + aarch64_max_tcg_initfn(obj); + } + return; } + + /* Ultimate fallback: -cpu max as cortex-a57. */ + assert(qtest_enabled()); + aarch64_aa32_a57_init(obj, !aarch64_enabled); } static const ARMCPUInfo arm_max_cpu = { From 9caff83f5b976da8285db55d363e861675e79777 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 670/860] target/arm: Pass ARMCPU to aarch64_aa32_a57_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's no reason to pass Object when ARMCPU is more appropriate. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20260812204435.295067-3-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu-max.c | 7 +++---- target/arm/cpu64.c | 2 +- target/arm/internals.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index 0a310d1b15..bd858f010c 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -16,9 +16,8 @@ #include "target/arm/internals.h" #include "target/arm/cpregs.h" -void aarch64_aa32_a57_init(Object *obj, bool aa32_only) +void aarch64_aa32_a57_init(ARMCPU *cpu, bool aa32_only) { - ARMCPU *cpu = ARM_CPU(obj); ARMISARegisters *isar = &cpu->isar; const bool aarch64_enabled = !aa32_only; @@ -208,7 +207,7 @@ static void cpu_max_initfn(Object *obj) * '-cpu max' for TCG: we currently do this as * "A57 with extra things" */ - aarch64_aa32_a57_init(obj, !aarch64_enabled); + aarch64_aa32_a57_init(cpu, !aarch64_enabled); if (!aarch64_enabled) { aa32_max_features(cpu); #ifdef CONFIG_USER_ONLY @@ -228,7 +227,7 @@ static void cpu_max_initfn(Object *obj) /* Ultimate fallback: -cpu max as cortex-a57. */ assert(qtest_enabled()); - aarch64_aa32_a57_init(obj, !aarch64_enabled); + aarch64_aa32_a57_init(cpu, !aarch64_enabled); } static const ARMCPUInfo arm_max_cpu = { diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 2816735577..e3ee0253a0 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -688,7 +688,7 @@ void aarch64_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) static void aarch64_a57_initfn(Object *obj) { - aarch64_aa32_a57_init(obj, false); + aarch64_aa32_a57_init(ARM_CPU(obj), false); } static void aarch64_a53_initfn(Object *obj) diff --git a/target/arm/internals.h b/target/arm/internals.h index 4026f67579..9c9196475b 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1813,7 +1813,7 @@ void aarch64_max_tcg_initfn(Object *obj); void aarch64_add_pauth_properties(Object *obj); void aarch64_add_sve_properties(Object *obj); void aarch64_add_sme_properties(Object *obj); -void aarch64_aa32_a57_init(Object *obj, bool aa32_only); +void aarch64_aa32_a57_init(ARMCPU *cpu, bool aa32_only); void aarch64_host_initfn(Object *obj); /* Return true if the gdbstub is presenting an AArch64 CPU */ From e5b092fa9251dec4f45f0f8a55ddd58f6cecac4d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 671/860] target/arm: Pass aarch64_enabled to aarch64_aa32_a57_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Invert aa32_only argument to what is actually used. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20260812204435.295067-4-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu-max.c | 7 +++---- target/arm/cpu64.c | 2 +- target/arm/internals.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index bd858f010c..edb5b9e6c3 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -16,10 +16,9 @@ #include "target/arm/internals.h" #include "target/arm/cpregs.h" -void aarch64_aa32_a57_init(ARMCPU *cpu, bool aa32_only) +void aarch64_aa32_a57_init(ARMCPU *cpu, bool aarch64_enabled) { ARMISARegisters *isar = &cpu->isar; - const bool aarch64_enabled = !aa32_only; cpu->dtb_compatible = "arm,cortex-a57"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -207,7 +206,7 @@ static void cpu_max_initfn(Object *obj) * '-cpu max' for TCG: we currently do this as * "A57 with extra things" */ - aarch64_aa32_a57_init(cpu, !aarch64_enabled); + aarch64_aa32_a57_init(cpu, aarch64_enabled); if (!aarch64_enabled) { aa32_max_features(cpu); #ifdef CONFIG_USER_ONLY @@ -227,7 +226,7 @@ static void cpu_max_initfn(Object *obj) /* Ultimate fallback: -cpu max as cortex-a57. */ assert(qtest_enabled()); - aarch64_aa32_a57_init(cpu, !aarch64_enabled); + aarch64_aa32_a57_init(cpu, aarch64_enabled); } static const ARMCPUInfo arm_max_cpu = { diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index e3ee0253a0..faaf3ab879 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -688,7 +688,7 @@ void aarch64_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) static void aarch64_a57_initfn(Object *obj) { - aarch64_aa32_a57_init(ARM_CPU(obj), false); + aarch64_aa32_a57_init(ARM_CPU(obj), true); } static void aarch64_a53_initfn(Object *obj) diff --git a/target/arm/internals.h b/target/arm/internals.h index 9c9196475b..5e7f41a50d 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1813,7 +1813,7 @@ void aarch64_max_tcg_initfn(Object *obj); void aarch64_add_pauth_properties(Object *obj); void aarch64_add_sve_properties(Object *obj); void aarch64_add_sme_properties(Object *obj); -void aarch64_aa32_a57_init(ARMCPU *cpu, bool aa32_only); +void aarch64_aa32_a57_init(ARMCPU *cpu, bool aa64_enabled); void aarch64_host_initfn(Object *obj); /* Return true if the gdbstub is presenting an AArch64 CPU */ From b5af4f6024023d5649c6ed248c837226491b3372 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 672/860] target/arm: Sink aarch64_aa32_a57_init into aarch64_max_tcg_initfn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-id: 20260812204435.295067-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-max.c | 2 +- target/arm/tcg/cpu64.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index edb5b9e6c3..93f24114a5 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -206,8 +206,8 @@ static void cpu_max_initfn(Object *obj) * '-cpu max' for TCG: we currently do this as * "A57 with extra things" */ - aarch64_aa32_a57_init(cpu, aarch64_enabled); if (!aarch64_enabled) { + aarch64_aa32_a57_init(cpu, false); aa32_max_features(cpu); #ifdef CONFIG_USER_ONLY /* diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 24f34947ec..5649b853a7 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1167,6 +1167,8 @@ void aarch64_max_tcg_initfn(Object *obj) uint64_t t; uint32_t u; + aarch64_aa32_a57_init(cpu, true); + SET_IDREG(isar, CLIDR, 0x8200123); /* 64KB L1 dcache */ cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 64 * KiB, 7); From aa517d9774d63ffe41066e4c26c7540ee1625140 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 673/860] target/arm: Split out aarch32_max_tcg_init 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é Message-id: 20260812204435.295067-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-max.c | 16 +--------------- target/arm/internals.h | 1 + target/arm/tcg/cpu32.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index 93f24114a5..7df7d015a9 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -202,22 +202,8 @@ static void cpu_max_initfn(Object *obj) } if (tcg_enabled()) { - /* - * '-cpu max' for TCG: we currently do this as - * "A57 with extra things" - */ if (!aarch64_enabled) { - aarch64_aa32_a57_init(cpu, false); - aa32_max_features(cpu); -#ifdef CONFIG_USER_ONLY - /* - * Break with true ARMv8 and add back old-style VFP short-vector - * support. Only do this for user-mode, where -cpu max is the default, - * so that older v6 and v7 programs are more likely to work without - * adjustment. - */ - cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); -#endif + aarch32_max_tcg_init(cpu); } else { aarch64_max_tcg_initfn(obj); } diff --git a/target/arm/internals.h b/target/arm/internals.h index 5e7f41a50d..f6b4e173a5 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1838,6 +1838,7 @@ uint32_t *arm_v7m_get_sp_ptr(CPUARMState *env, bool secure, bool el_is_in_host(CPUARMState *env, int el); void aa32_max_features(ARMCPU *cpu); +void aarch32_max_tcg_init(ARMCPU *cpu); int exception_target_el(CPUARMState *env); bool arm_singlestep_active(CPUARMState *env); bool arm_generate_debug_exceptions(CPUARMState *env); diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 8220d785f5..e1ff074b47 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -748,3 +748,18 @@ static void arm_tcg_cpu_register_types(void) type_init(arm_tcg_cpu_register_types) #endif /* !CONFIG_USER_ONLY || !TARGET_AARCH64 */ + +void aarch32_max_tcg_init(ARMCPU *cpu) +{ + aarch64_aa32_a57_init(cpu, false); + aa32_max_features(cpu); +#ifdef CONFIG_USER_ONLY + /* + * Break with true ARMv8 and add back old-style VFP short-vector + * support. Only do this for user-mode, where -cpu max is the default, + * so that older v6 and v7 programs are more likely to work without + * adjustment. + */ + cpu->isar.mvfr0 = FIELD_DP32(cpu->isar.mvfr0, MVFR0, FPSHVEC, 1); +#endif +} From a2c25ce4138926c8ab44738263c35e8c329b9961 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 674/860] target/arm: Move aa32_max_features to tcg/cpu32.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is unused without TCG, so move. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260812204435.295067-7-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpu-max.c | 96 ------------------------------------------ target/arm/tcg/cpu32.c | 96 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index 7df7d015a9..8d968336a8 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -85,102 +85,6 @@ void aarch64_aa32_a57_init(ARMCPU *cpu, bool aarch64_enabled) define_cortex_a72_a57_a53_cp_reginfo(cpu); } -/* Share AArch32 -cpu max features with AArch64. */ -void aa32_max_features(ARMCPU *cpu) -{ - uint32_t t; - ARMISARegisters *isar = &cpu->isar; - - /* Add additional features supported by QEMU */ - t = GET_IDREG(isar, ID_ISAR5); - t = FIELD_DP32(t, ID_ISAR5, AES, 2); /* FEAT_PMULL */ - t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); /* FEAT_SHA1 */ - t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); /* FEAT_SHA256 */ - t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); - t = FIELD_DP32(t, ID_ISAR5, RDM, 1); /* FEAT_RDM */ - t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); /* FEAT_FCMA */ - SET_IDREG(isar, ID_ISAR5, t); - - t = GET_IDREG(isar, ID_ISAR6); - t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); /* FEAT_JSCVT */ - t = FIELD_DP32(t, ID_ISAR6, DP, 1); /* Feat_DotProd */ - t = FIELD_DP32(t, ID_ISAR6, FHM, 1); /* FEAT_FHM */ - t = FIELD_DP32(t, ID_ISAR6, SB, 1); /* FEAT_SB */ - t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); /* FEAT_SPECRES */ - t = FIELD_DP32(t, ID_ISAR6, BF16, 1); /* FEAT_AA32BF16 */ - t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); /* FEAT_AA32I8MM */ - SET_IDREG(isar, ID_ISAR6, t); - - t = cpu->isar.mvfr1; - t = FIELD_DP32(t, MVFR1, FPHP, 3); /* FEAT_FP16 */ - t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* FEAT_FP16 */ - cpu->isar.mvfr1 = t; - - t = cpu->isar.mvfr2; - t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */ - t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ - cpu->isar.mvfr2 = t; - - FIELD_DP32_IDREG(isar, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ - - t = GET_IDREG(isar, ID_MMFR4); - t = FIELD_DP32(t, ID_MMFR4, HPDS, 2); /* FEAT_HPDS2 */ - t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ - t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* FEAT_TTCNP */ - t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX */ - t = FIELD_DP32(t, ID_MMFR4, EVT, 2); /* FEAT_EVT2 */ - SET_IDREG(isar, ID_MMFR4, t); - - FIELD_DP32_IDREG(isar, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ - - t = GET_IDREG(isar, ID_PFR0); - t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CSV2 */ - t = FIELD_DP32(t, ID_PFR0, DIT, 1); /* FEAT_DIT */ - t = FIELD_DP32(t, ID_PFR0, RAS, 1); /* FEAT_RAS */ - SET_IDREG(isar, ID_PFR0, t); - - t = GET_IDREG(isar, ID_PFR2); - t = FIELD_DP32(t, ID_PFR2, CSV3, 1); /* FEAT_CSV3 */ - t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ - SET_IDREG(isar, ID_PFR2, t); - - t = GET_IDREG(isar, ID_DFR0); - t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP32(t, ID_DFR0, COPSDBG, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ - SET_IDREG(isar, ID_DFR0, t); - - /* Debug ID registers. */ - - /* Bit[15] is RES1, Bit[13] and Bits[11:0] are RES0. */ - t = 0x00008000; - t = FIELD_DP32(t, DBGDIDR, SE_IMP, 1); - t = FIELD_DP32(t, DBGDIDR, NSUHD_IMP, 1); - t = FIELD_DP32(t, DBGDIDR, VERSION, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP32(t, DBGDIDR, CTX_CMPS, 1); - t = FIELD_DP32(t, DBGDIDR, BRPS, 5); - t = FIELD_DP32(t, DBGDIDR, WRPS, 3); - cpu->isar.dbgdidr = t; - - t = 0; - t = FIELD_DP32(t, DBGDEVID, PCSAMPLE, 3); - t = FIELD_DP32(t, DBGDEVID, WPADDRMASK, 1); - t = FIELD_DP32(t, DBGDEVID, BPADDRMASK, 15); - t = FIELD_DP32(t, DBGDEVID, VECTORCATCH, 0); - t = FIELD_DP32(t, DBGDEVID, VIRTEXTNS, 1); - t = FIELD_DP32(t, DBGDEVID, DOUBLELOCK, 1); - t = FIELD_DP32(t, DBGDEVID, AUXREGS, 0); - t = FIELD_DP32(t, DBGDEVID, CIDMASK, 0); - cpu->isar.dbgdevid = t; - - /* Bits[31:4] are RES0. */ - t = 0; - t = FIELD_DP32(t, DBGDEVID1, PCSROFFSET, 2); - cpu->isar.dbgdevid1 = t; - - FIELD_DP32_IDREG(isar, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ -} - /* * -cpu max: a CPU with as many features enabled as our emulation supports. * The version of '-cpu max' for qemu-system-aarch64 is defined in cpu64.c; diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index e1ff074b47..d5dd236152 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -749,6 +749,102 @@ type_init(arm_tcg_cpu_register_types) #endif /* !CONFIG_USER_ONLY || !TARGET_AARCH64 */ +/* Share AArch32 -cpu max features with AArch64. */ +void aa32_max_features(ARMCPU *cpu) +{ + uint32_t t; + ARMISARegisters *isar = &cpu->isar; + + /* Add additional features supported by QEMU */ + t = GET_IDREG(isar, ID_ISAR5); + t = FIELD_DP32(t, ID_ISAR5, AES, 2); /* FEAT_PMULL */ + t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); /* FEAT_SHA1 */ + t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); /* FEAT_SHA256 */ + t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); + t = FIELD_DP32(t, ID_ISAR5, RDM, 1); /* FEAT_RDM */ + t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); /* FEAT_FCMA */ + SET_IDREG(isar, ID_ISAR5, t); + + t = GET_IDREG(isar, ID_ISAR6); + t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); /* FEAT_JSCVT */ + t = FIELD_DP32(t, ID_ISAR6, DP, 1); /* Feat_DotProd */ + t = FIELD_DP32(t, ID_ISAR6, FHM, 1); /* FEAT_FHM */ + t = FIELD_DP32(t, ID_ISAR6, SB, 1); /* FEAT_SB */ + t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); /* FEAT_SPECRES */ + t = FIELD_DP32(t, ID_ISAR6, BF16, 1); /* FEAT_AA32BF16 */ + t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); /* FEAT_AA32I8MM */ + SET_IDREG(isar, ID_ISAR6, t); + + t = cpu->isar.mvfr1; + t = FIELD_DP32(t, MVFR1, FPHP, 3); /* FEAT_FP16 */ + t = FIELD_DP32(t, MVFR1, SIMDHP, 2); /* FEAT_FP16 */ + cpu->isar.mvfr1 = t; + + t = cpu->isar.mvfr2; + t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */ + t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ + cpu->isar.mvfr2 = t; + + FIELD_DP32_IDREG(isar, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ + + t = GET_IDREG(isar, ID_MMFR4); + t = FIELD_DP32(t, ID_MMFR4, HPDS, 2); /* FEAT_HPDS2 */ + t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ + t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* FEAT_TTCNP */ + t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX */ + t = FIELD_DP32(t, ID_MMFR4, EVT, 2); /* FEAT_EVT2 */ + SET_IDREG(isar, ID_MMFR4, t); + + FIELD_DP32_IDREG(isar, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ + + t = GET_IDREG(isar, ID_PFR0); + t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CSV2 */ + t = FIELD_DP32(t, ID_PFR0, DIT, 1); /* FEAT_DIT */ + t = FIELD_DP32(t, ID_PFR0, RAS, 1); /* FEAT_RAS */ + SET_IDREG(isar, ID_PFR0, t); + + t = GET_IDREG(isar, ID_PFR2); + t = FIELD_DP32(t, ID_PFR2, CSV3, 1); /* FEAT_CSV3 */ + t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ + SET_IDREG(isar, ID_PFR2, t); + + t = GET_IDREG(isar, ID_DFR0); + t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, ID_DFR0, COPSDBG, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ + SET_IDREG(isar, ID_DFR0, t); + + /* Debug ID registers. */ + + /* Bit[15] is RES1, Bit[13] and Bits[11:0] are RES0. */ + t = 0x00008000; + t = FIELD_DP32(t, DBGDIDR, SE_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, NSUHD_IMP, 1); + t = FIELD_DP32(t, DBGDIDR, VERSION, 10); /* FEAT_Debugv8p8 */ + t = FIELD_DP32(t, DBGDIDR, CTX_CMPS, 1); + t = FIELD_DP32(t, DBGDIDR, BRPS, 5); + t = FIELD_DP32(t, DBGDIDR, WRPS, 3); + cpu->isar.dbgdidr = t; + + t = 0; + t = FIELD_DP32(t, DBGDEVID, PCSAMPLE, 3); + t = FIELD_DP32(t, DBGDEVID, WPADDRMASK, 1); + t = FIELD_DP32(t, DBGDEVID, BPADDRMASK, 15); + t = FIELD_DP32(t, DBGDEVID, VECTORCATCH, 0); + t = FIELD_DP32(t, DBGDEVID, VIRTEXTNS, 1); + t = FIELD_DP32(t, DBGDEVID, DOUBLELOCK, 1); + t = FIELD_DP32(t, DBGDEVID, AUXREGS, 0); + t = FIELD_DP32(t, DBGDEVID, CIDMASK, 0); + cpu->isar.dbgdevid = t; + + /* Bits[31:4] are RES0. */ + t = 0; + t = FIELD_DP32(t, DBGDEVID1, PCSROFFSET, 2); + cpu->isar.dbgdevid1 = t; + + FIELD_DP32_IDREG(isar, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ +} + void aarch32_max_tcg_init(ARMCPU *cpu) { aarch64_aa32_a57_init(cpu, false); From 8d346a7109ac692edc4cb80c6a450a0076ed87af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 675/860] target/arm: Annotate arch revisions in aarch64_max_tcg_initfn Annotate the minimum revion from which each feature is OPTIONAL. Modulo some sorting of properties at the end, comment changes only. Signed-off-by: Richard Henderson Message-id: 20260812204435.295067-10-richard.henderson@linaro.org [PMM: fixed two minor comment issues] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/cpu64.c | 303 +++++++++++++++++++++-------------------- 1 file changed, 159 insertions(+), 144 deletions(-) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 5649b853a7..9ce514dda8 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1226,199 +1226,202 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, CTR_EL0, DIC, 1); cpu->ctr = t; + /* + * Below, note the revision from which the feature is OPTIONAL. + */ t = GET_IDREG(isar, ID_AA64ISAR0); - t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */ - 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, 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 */ - t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1); /* FEAT_SM4 */ - t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1); /* FEAT_DotProd */ - t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1); /* FEAT_FHM */ - t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* FEAT_FlagM2 */ - t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* FEAT_TLBIRANGE */ - t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* FEAT_RNG */ + t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* v8.0: FEAT_PMULL */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* v8.0: FEAT_SHA1 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* v8.1: FEAT_SHA512 */ + t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* v8.0: FEAT_CRC32 */ + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* v9.3: FEAT_LSE128 */ + t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* v8.0: FEAT_RDM */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* v8.1: FEAT_SHA3 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* v8.1: FEAT_SM3 */ + t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1); /* v8.1: FEAT_SM4 */ + t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1); /* v8.1: FEAT_DotProd */ + t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1); /* v8.1: FEAT_FHM */ + t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* v8.4: FEAT_FlagM2 */ + t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* v8.3: FEAT_TLBIRANGE */ + t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* v8.4: FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR0, t); t = GET_IDREG(isar, ID_AA64ISAR1); - t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* FEAT_DPB2 */ - t = FIELD_DP64(t, ID_AA64ISAR1, APA, PauthFeat_FPACCOMBINED); + t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* v8.1: FEAT_DPB2 */ + t = FIELD_DP64(t, ID_AA64ISAR1, APA, PauthFeat_FPACCOMBINED); /* v8.2 */ t = FIELD_DP64(t, ID_AA64ISAR1, API, 1); - t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); /* FEAT_JSCVT */ - t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); /* FEAT_FCMA */ - t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* FEAT_LRCPC2 */ - t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); /* FEAT_FRINTTS */ - t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); /* FEAT_SB */ - t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); /* FEAT_SPECRES */ - t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 2); /* FEAT_BF16, FEAT_EBF16 */ - t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* FEAT_DGH */ - t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ - t = FIELD_DP64(t, ID_AA64ISAR1, XS, 1); /* FEAT_XS */ + t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); /* v8.2: FEAT_JSCVT */ + t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); /* v8.2: FEAT_FCMA */ + t = FIELD_DP64(t, ID_AA64ISAR1, LRCPC, 2); /* v8.2: FEAT_LRCPC2 */ + t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); /* v8.4: FEAT_FRINTTS */ + t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); /* v8.0: FEAT_SB */ + t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); /* v8.0: FEAT_SPECRES */ + t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 2); /* v8.2: FEAT_EBF16 */ + t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* v8.0: FEAT_DGH */ + t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* v8.1: FEAT_I8MM */ + t = FIELD_DP64(t, ID_AA64ISAR1, XS, 1); /* v8.6: FEAT_XS */ SET_IDREG(isar, ID_AA64ISAR1, t); t = GET_IDREG(isar, ID_AA64ISAR2); - t = FIELD_DP64(t, ID_AA64ISAR2, RPRES, 1); /* FEAT_RPRES */ - 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, 2); /* FEAT_CSSC, FEAT_CMPBR */ - t = FIELD_DP64(t, ID_AA64ISAR2, LUT, 1); /* FEAT_LUT */ - t = FIELD_DP64(t, ID_AA64ISAR2, ATS1A, 1); /* FEAT_ATS1A */ + t = FIELD_DP64(t, ID_AA64ISAR2, RPRES, 1); /* v8.6: FEAT_RPRES */ + t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* v8.7: FEAT_MOPS */ + t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* v8.7: FEAT_HBC */ + t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* v8.6: FEAT_WFxT */ + t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 2); /* v9.3: FEAT_CMPBR */ + t = FIELD_DP64(t, ID_AA64ISAR2, LUT, 1); /* v9.2: FEAT_LUT */ + t = FIELD_DP64(t, ID_AA64ISAR2, ATS1A, 1); /* v8.8: FEAT_ATS1A */ SET_IDREG(isar, ID_AA64ISAR2, t); t = GET_IDREG(isar, ID_AA64ISAR3); - t = FIELD_DP64(t, ID_AA64ISAR3, FAMINMAX, 1); /* FEAT_FAMINMAX */ - t = FIELD_DP64(t, ID_AA64ISAR3, FPRCVT, 1); /* FEAT_FPRCVT */ + t = FIELD_DP64(t, ID_AA64ISAR3, FAMINMAX, 1); /* v9.2: FEAT_FAMINMAX */ + t = FIELD_DP64(t, ID_AA64ISAR3, FPRCVT, 1); /* v9.5: FEAT_FPRCVT */ SET_IDREG(isar, ID_AA64ISAR3, t); t = GET_IDREG(isar, ID_AA64PFR0); - t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* FEAT_FP16 */ - t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* FEAT_FP16 */ - t = FIELD_DP64(t, ID_AA64PFR0, RAS, 2); /* FEAT_RASv1p1 + FEAT_DoubleFault */ - t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); - t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); /* FEAT_SEL2 */ - t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* FEAT_DIT */ - t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 3); /* FEAT_CSV2_3 */ - t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* FEAT_CSV3 */ + t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* v8.2: FEAT_FP16 */ + t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* v8.2: FEAT_FP16 */ + t = FIELD_DP64(t, ID_AA64PFR0, RAS, 2); /* v8.3: FEAT_DoubleFault */ + t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); /* v8.2: FEAT_SVE */ + t = FIELD_DP64(t, ID_AA64PFR0, SEL2, 1); /* v8.3: FEAT_SEL2 */ + t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* v8.3: FEAT_DIT */ + t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 3); /* v8.0: FEAT_CSV2_3 */ + t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* v8.0: FEAT_CSV3 */ SET_IDREG(isar, ID_AA64PFR0, t); t = GET_IDREG(isar, ID_AA64PFR1); - t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); /* FEAT_BTI */ - t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* FEAT_SSBS2 */ + t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); /* v8.4: FEAT_BTI */ + t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* v8.0: FEAT_SSBS2 */ /* * Begin with full support for MTE. This will be downgraded to MTE=0 * during realize if the board provides no tag memory, much like * we do for EL2 with the virtualization=on property. */ - t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* FEAT_MTE3 */ - t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ - t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* FEAT_SME2 */ - t = FIELD_DP64(t, ID_AA64PFR1, RNDR_TRAP, 1); /* FEAT_RNG_TRAP */ - 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 */ - /* FEAT_MTE_NO_ADDRESS_TAGS + FEAT_MTE_CANONICAL_TAGS */ + t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* v8.5: FEAT_MTE3 */ + t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* v8.3: FEAT_DoubleFault */ + t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* v9.2: FEAT_SME2 */ + t = FIELD_DP64(t, ID_AA64PFR1, RNDR_TRAP, 1); /* v8.4: FEAT_RNG_TRAP */ + t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* v8.0: FEAT_CSV2_3 */ + t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* v8.7: FEAT_NMI */ + t = FIELD_DP64(t, ID_AA64PFR1, GCS, 1); /* v9.3: FEAT_GCS */ + /* v8.7: FEAT_MTE_NO_ADDRESS_TAGS + FEAT_MTE_CANONICAL_TAGS */ t = FIELD_DP64(t, ID_AA64PFR1, MTEX, 1); SET_IDREG(isar, ID_AA64PFR1, t); t = GET_IDREG(isar, ID_AA64PFR2); - t = FIELD_DP64(t, ID_AA64PFR2, FPMR, 1); /* FEAT_FPMR */ - t = FIELD_DP64(t, ID_AA64PFR2, MTEFAR, 1); /* FEAT_MTE_TAGGED_FAR */ - t = FIELD_DP64(t, ID_AA64PFR2, MTESTOREONLY, 1); /* FEAT_MTE_STORE_ONLY */ - t = FIELD_DP64(t, ID_AA64PFR2, MTEPERM, 1); /* FEAT_MTE_PERM */ + t = FIELD_DP64(t, ID_AA64PFR2, FPMR, 1); /* v9.2: FEAT_FPMR */ + t = FIELD_DP64(t, ID_AA64PFR2, MTEFAR, 1); /* v8.7: FEAT_MTE_TAGGED_FAR */ + t = FIELD_DP64(t, ID_AA64PFR2, MTESTOREONLY, 1); /* v8.7: FEAT_MTE_STORE_ONLY */ + t = FIELD_DP64(t, ID_AA64PFR2, MTEPERM, 1); /* v8.7: FEAT_MTE_PERM */ SET_IDREG(isar, ID_AA64PFR2, t); t = GET_IDREG(isar, ID_AA64MMFR0); - t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* 16k pages supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* 16k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* 64k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */ - t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* FEAT_FGT */ - t = FIELD_DP64(t, ID_AA64MMFR0, ECV, 2); /* FEAT_ECV */ + t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* v8.1: FEAT_LPA: 52 bits */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* v8.0: FEAT_TGran16K */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* v8.0: FEAT_S2TGran16K */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN64_2, 2); /* v8.0: FEAT_S2TGran64K */ + t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* v8.0: FEAT_S2TGran4K */ + t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* v8.5: FEAT_FGT */ + t = FIELD_DP64(t, ID_AA64MMFR0, ECV, 2); /* v8.5: FEAT_ECV_POFF */ SET_IDREG(isar, ID_AA64MMFR0, t); t = GET_IDREG(isar, ID_AA64MMFR1); - t = FIELD_DP64(t, ID_AA64MMFR1, HAFDBS, 2); /* FEAT_HAFDBS */ - t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* FEAT_VMID16 */ - t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); /* FEAT_VHE */ - t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 2); /* FEAT_HPDS2 */ - t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* FEAT_LOR */ - t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 3); /* FEAT_PAN3 */ - t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* FEAT_XNX */ - t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 2); /* FEAT_ETS2 */ - t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* FEAT_HCX */ - t = FIELD_DP64(t, ID_AA64MMFR1, AFP, 1); /* FEAT_AFP */ - t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* FEAT_TIDCP1 */ - t = FIELD_DP64(t, ID_AA64MMFR1, CMOW, 1); /* FEAT_CMOW */ + t = FIELD_DP64(t, ID_AA64MMFR1, HAFDBS, 2); /* v8.0: FEAT_HAFDBS */ + t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* v8.0: FEAT_VMID16 */ + t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); /* v8.0: FEAT_VHE */ + t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 2); /* v8.1: FEAT_HPDS2 */ + t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); /* v8.0: FEAT_LOR */ + t = FIELD_DP64(t, ID_AA64MMFR1, PAN, 3); /* v8.1: FEAT_PAN3 */ + t = FIELD_DP64(t, ID_AA64MMFR1, XNX, 1); /* v8.1: FEAT_XNX */ + t = FIELD_DP64(t, ID_AA64MMFR1, ETS, 2); /* v8.0: FEAT_ETS2 */ + t = FIELD_DP64(t, ID_AA64MMFR1, HCX, 1); /* v8.6: FEAT_HCX */ + t = FIELD_DP64(t, ID_AA64MMFR1, AFP, 1); /* v8.6: FEAT_AFP */ + t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* v8.7: FEAT_TIDCP1 */ + t = FIELD_DP64(t, ID_AA64MMFR1, CMOW, 1); /* v8.7: FEAT_CMOW */ SET_IDREG(isar, ID_AA64MMFR1, t); t = GET_IDREG(isar, ID_AA64MMFR2); - t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* FEAT_TTCNP */ - t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ - t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ - t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* FEAT_LVA */ - t = FIELD_DP64(t, ID_AA64MMFR2, NV, 2); /* FEAT_NV2 */ - t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* FEAT_TTST */ - t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* FEAT_LSE2 */ - t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* FEAT_IDST */ - t = FIELD_DP64(t, ID_AA64MMFR2, FWB, 1); /* FEAT_S2FWB */ - t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* FEAT_TTL */ - t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* FEAT_BBM at level 2 */ - t = FIELD_DP64(t, ID_AA64MMFR2, EVT, 2); /* FEAT_EVT */ - t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ + t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* v8.1: FEAT_TTCNP */ + t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* v8.1: FEAT_UAO */ + t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* v8.1: FEAT_IESB */ + t = FIELD_DP64(t, ID_AA64MMFR2, VARANGE, 1); /* v8.1: FEAT_LVA */ + t = FIELD_DP64(t, ID_AA64MMFR2, NV, 2); /* v8.3: FEAT_NV2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, ST, 1); /* v8.3: FEAT_TTST */ + t = FIELD_DP64(t, ID_AA64MMFR2, AT, 1); /* v8.2: FEAT_LSE2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, IDS, 1); /* v8.3: FEAT_IDST */ + t = FIELD_DP64(t, ID_AA64MMFR2, FWB, 1); /* v8.3: FEAT_S2FWB */ + t = FIELD_DP64(t, ID_AA64MMFR2, TTL, 1); /* v8.3: FEAT_TTL */ + t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* v8.3: FEAT_BBML2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, EVT, 2); /* v8.2: FEAT_EVT2 */ + t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* v8.4: FEAT_E0PD */ 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, 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 */ - t = FIELD_DP64(t, ID_AA64MMFR3, AIE, 1); /* FEAT_AIE */ + t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* v8.0: FEAT_TCR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* v8.0: FEAT_SCTLR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, MEC, 1); /* v9.2: FEAT_MEC */ + t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* v8.2: FEAT_FPACC_SPEC */ + t = FIELD_DP64(t, ID_AA64MMFR3, S1PIE, 1); /* v8.8: FEAT_S1PIE */ + t = FIELD_DP64(t, ID_AA64MMFR3, S2PIE, 1); /* v8.8: FEAT_S2PIE */ + t = FIELD_DP64(t, ID_AA64MMFR3, AIE, 1); /* v8.8: FEAT_AIE */ SET_IDREG(isar, ID_AA64MMFR3, t); t = GET_IDREG(isar, ID_AA64MMFR4); - t = FIELD_DP64(t, ID_AA64MMFR4, ASID2, 1); /* FEAT_ASID2 */ + t = FIELD_DP64(t, ID_AA64MMFR4, ASID2, 1); /* v9.4: FEAT_ASID2 */ SET_IDREG(isar, ID_AA64MMFR4, t); t = GET_IDREG(isar, ID_AA64ZFR0); - t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* FEAT_SVE2p1 */ - t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ - t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* FEAT_SVE_BitPerm */ - t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 2); /* FEAT_BF16, FEAT_EBF16 */ - t = FIELD_DP64(t, ID_AA64ZFR0, B16B16, 1); /* FEAT_SVE_B16B16 */ - t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* FEAT_SVE_SHA3 */ - t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* FEAT_SVE_SM4 */ - t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* FEAT_I8MM */ - t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* FEAT_F32MM */ - t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* FEAT_F64MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* v9.2: FEAT_SVE2p1 */ + t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* v9.0: FEAT_SVE_PMULL128 */ + t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* v9.0: FEAT_SVE_BitPerm */ + t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 2); /* v8.2: FEAT_EBF16 */ + t = FIELD_DP64(t, ID_AA64ZFR0, B16B16, 1); /* v9.2: FEAT_SVE_B16B16 */ + t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* v9.0: FEAT_SVE_SHA3 */ + t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* v9.0: FEAT_SVE_SM4 */ + t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* v8.1: FEAT_I8MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* v8.2: FEAT_F32MM */ + t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* v8.2: FEAT_F64MM */ SET_IDREG(isar, ID_AA64ZFR0, t); t = GET_IDREG(isar, ID_AA64DFR0); - t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 10); /* FEAT_Debugv8p8 */ - t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 6); /* FEAT_PMUv3p5 */ - t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* FEAT_HPMN0 */ + t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 10); /* v8.7: FEAT_Debugv8p8 */ + t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 6); /* v8.4: FEAT_PMUv3p5 */ + t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* v8.5: FEAT_HPMN0 */ SET_IDREG(isar, ID_AA64DFR0, t); t = GET_IDREG(isar, ID_AA64SMFR0); - t = FIELD_DP64(t, ID_AA64SMFR0, SMOP4, 1); /* FEAT_SME_MOP4 */ - t = FIELD_DP64(t, ID_AA64SMFR0, STMOP, 1); /* FEAT_SME_TMOP */ - t = FIELD_DP64(t, ID_AA64SMFR0, SFEXPA, 1); /* FEAT_SSVE_FEXPA */ - t = FIELD_DP64(t, ID_AA64SMFR0, AES, 1); /* FEAT_SSVE_AES */ - t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP2, 1); /* FEAT_SSVE_FP8DOT2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP4, 1); /* FEAT_SSVE_FP8DOT4 */ - t = FIELD_DP64(t, ID_AA64SMFR0, SF8FMA, 1); /* FEAT_SSVE_FP8FMA */ - t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, BI32I32, 1); /* FEAT_SME2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* FEAT_SME */ - t = FIELD_DP64(t, ID_AA64SMFR0, F8F32, 1); /* FEAT_SME_F8F32 */ - t = FIELD_DP64(t, ID_AA64SMFR0, F8F16, 1); /* FEAT_SME_F8F16 */ - t = FIELD_DP64(t, ID_AA64SMFR0, F16F16, 1); /* FEAT_SME_F16F16 */ - t = FIELD_DP64(t, ID_AA64SMFR0, B16B16, 1); /* FEAT_SME_B16B16 */ - t = FIELD_DP64(t, ID_AA64SMFR0, I16I32, 5); /* FEAT_SME2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* FEAT_SME_F64F64 */ - t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* FEAT_SME_I16I64 */ - t = FIELD_DP64(t, ID_AA64SMFR0, SMEVER, 2); /* FEAT_SME2p1 */ - t = FIELD_DP64(t, ID_AA64SMFR0, LUTv2, 1); /* FEAT_SME_LUTv2 */ - t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* FEAT_SME_FA64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SMOP4, 1); /* v9.4: FEAT_SME_MOP4 */ + t = FIELD_DP64(t, ID_AA64SMFR0, STMOP, 1); /* v9.4: FEAT_SME_TMOP */ + t = FIELD_DP64(t, ID_AA64SMFR0, SFEXPA, 1); /* v9.4: FEAT_SSVE_FEXPA */ + t = FIELD_DP64(t, ID_AA64SMFR0, AES, 1); /* v9.5: FEAT_SSVE_AES */ + t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP2, 1); /* v9.2: FEAT_SSVE_FP8DOT2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SF8DP4, 1); /* v9.2: FEAT_SSVE_FP8DOT4 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SF8FMA, 1); /* v9.2: FEAT_SSVE_FP8FMA */ + t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, BI32I32, 1); /* v9.2: FEAT_SME2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* v9.2: FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F8F32, 1); /* v9.2: FEAT_SME_F8F32 */ + t = FIELD_DP64(t, ID_AA64SMFR0, F8F16, 1); /* v9.2: FEAT_SME_F8F16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, F16F16, 1); /* v9.2: FEAT_SME_F16F16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, B16B16, 1); /* v9.2: FEAT_SME_B16B16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, I16I32, 5); /* v9.2: FEAT_SME2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* v9.2: FEAT_SME_F64F64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* v9.2: FEAT_SME_I16I64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SMEVER, 2); /* v9.2: FEAT_SME2p1 */ + t = FIELD_DP64(t, ID_AA64SMFR0, LUTv2, 1); /* v9.2: FEAT_SME_LUTv2 */ + t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* v9.2: FEAT_SME_FA64 */ SET_IDREG(isar, ID_AA64SMFR0, t); t = GET_IDREG(isar, ID_AA64FPFR0); - t = FIELD_DP64(t, ID_AA64FPFR0, F8E5M2, 1); /* FEAT_FP8 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8E4M3, 1); /* FEAT_FP8 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8MM4, 1); /* FEAT_F8F16MM */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8MM8, 1); /* FEAT_F8F32MM */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8DP2, 1); /* FEAT_FP8DOT2 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8DP4, 1); /* FEAT_FP8DOT4 */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8FMA, 1); /* FEAT_FP8FMA */ - t = FIELD_DP64(t, ID_AA64FPFR0, F8CVT, 1); /* FEAT_FP8 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8E5M2, 1); /* v9.2: FEAT_FP8 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8E4M3, 1); /* v9.2: FEAT_FP8 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8MM4, 1); /* v9.2: FEAT_F8F16MM */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8MM8, 1); /* v9.2: FEAT_F8F32MM */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8DP2, 1); /* v9.2: FEAT_FP8DOT2 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8DP4, 1); /* v9.2: FEAT_FP8DOT4 */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8FMA, 1); /* v9.2: FEAT_FP8FMA */ + t = FIELD_DP64(t, ID_AA64FPFR0, F8CVT, 1); /* v9.2: FEAT_FP8 */ SET_IDREG(isar, ID_AA64FPFR0, t); /* Replicate the same data to the 32-bit id registers. */ @@ -1432,19 +1435,31 @@ void aarch64_max_tcg_initfn(Object *obj) cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ set_dczid_bs(cpu, 7); /* 512 bytes */ #endif + + /* v8.4: FEAT_MTE2 */ cpu->gm_blocksize = 6; /* 256 bytes */ + /* v8.2: FEAT_SVE */ cpu->sve_vq.supported = MAKE_64BIT_MASK(0, ARM_MAX_VQ); - cpu->sme_vq.supported = SVE_VQ_POW2_MAP; - - aarch64_add_pauth_properties(obj); aarch64_add_sve_properties(obj); - aarch64_add_sme_properties(obj); object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq, cpu_max_set_sve_max_vq, NULL, NULL); + + /* v9.2: FEAT_SME */ + cpu->sme_vq.supported = SVE_VQ_POW2_MAP; + aarch64_add_sme_properties(obj); + + /* v8.2: FEAT_PAuth2 */ + aarch64_add_pauth_properties(obj); + + /* v9.1: FEAT_RME */ object_property_add_bool(obj, "x-rme", cpu_arm_get_rme, cpu_arm_set_rme); + + /* v9.4: FEAT_RME_GPC2 */ object_property_add(obj, "x-l0gptsz", "uint32", cpu_max_get_l0gptsz, cpu_max_set_l0gptsz, NULL, NULL); + + /* v8.6: FEAT_LPA2 */ qdev_property_add_static(DEVICE(obj), &arm_cpu_lpa2_property); } From 4b263689baafeb3976a0cdeef35deaea8f0190e9 Mon Sep 17 00:00:00 2001 From: Simon Xu Date: Tue, 18 Aug 2026 10:39:08 +0100 Subject: [PATCH 676/860] hw/misc/iotkit-secctl.c: fix AHB secure read Fix AHB secure privilege to read AHB instead of APB. Fixes: b3717c23e1c0 ("hw/misc/iotkit-secctl: Add handling for PPCs") Reviewed-by: Owen Giles Reviewed-by: Robert Elliott Signed-off-by: Simon Xu Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/iotkit-secctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 54bfe1ba59..93753c2ced 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -195,7 +195,7 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_AHBSPPPCEXP1: case A_AHBSPPPCEXP2: case A_AHBSPPPCEXP3: - r = s->apbexp[offset_to_ppc_idx(offset)].sp; + r = s->ahbexp[offset_to_ppc_idx(offset)].sp; break; case A_APBSPPPC0: case A_APBSPPPC1: From b4f6f672a3e100706ddc249c26c2c149cdac1636 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 12:15:38 -0700 Subject: [PATCH 677/860] tcg: Export tcg_gen_ussub_i{32,64,tl} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move from tcg-op-gvec.c to tcg-op.c. Use a temporary, to cover the possibility of operand overlap. (Cc for stable as this is a prerequisite for the bug fix in the next commit.) Cc: qemu-stable@nongnu.org Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260811191540.79882-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- include/tcg/tcg-op-common.h | 2 ++ include/tcg/tcg-op.h | 2 ++ tcg/tcg-op-gvec.c | 14 -------------- tcg/tcg-op.c | 20 ++++++++++++++++++++ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index f7b6aadb50..9b321f959c 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -163,6 +163,7 @@ void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_ussub_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_abs_i32(TCGv_i32, TCGv_i32); void tcg_gen_revbit8_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_revbit32_i32(TCGv_i32 ret, TCGv_i32 arg); @@ -276,6 +277,7 @@ void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_ussub_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_abs_i64(TCGv_i64, TCGv_i64); void tcg_gen_revbit8_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_revbit32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 568b78fe81..3721164236 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -153,6 +153,7 @@ typedef TCGv_i64 TCGv; #define tcg_gen_umin_tl tcg_gen_umin_i64 #define tcg_gen_smax_tl tcg_gen_smax_i64 #define tcg_gen_umax_tl tcg_gen_umax_i64 +#define tcg_gen_ussub_tl tcg_gen_ussub_i64 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i64 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i64 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i64 @@ -275,6 +276,7 @@ typedef TCGv_i64 TCGv; #define tcg_gen_umin_tl tcg_gen_umin_i32 #define tcg_gen_smax_tl tcg_gen_smax_i32 #define tcg_gen_umax_tl tcg_gen_umax_i32 +#define tcg_gen_ussub_tl tcg_gen_ussub_i32 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i32 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i32 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i32 diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index bc323e2500..27f3807b00 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -2399,20 +2399,6 @@ void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); } -static void tcg_gen_ussub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - TCGv_i32 min = tcg_constant_i32(0); - tcg_gen_sub_i32(d, a, b); - tcg_gen_movcond_i32(TCG_COND_LTU, d, a, b, min, d); -} - -static void tcg_gen_ussub_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - TCGv_i64 min = tcg_constant_i64(0); - tcg_gen_sub_i64(d, a, b); - tcg_gen_movcond_i64(TCG_COND_LTU, d, a, b, min, d); -} - void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index e28944cf72..28d3b2a847 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1328,6 +1328,16 @@ void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) } } +void tcg_gen_ussub_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + TCGv_i32 t = tcg_temp_ebb_new_i32(); + TCGv_i32 z = tcg_constant_i32(0); + + tcg_gen_sub_i32(t, a, b); + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, z, t); + tcg_temp_free_i32(t); +} + void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) { TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -2523,6 +2533,16 @@ void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) } } +void tcg_gen_ussub_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + TCGv_i64 t = tcg_temp_ebb_new_i64(); + TCGv_i64 z = tcg_constant_i64(0); + + tcg_gen_sub_i64(t, a, b); + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, z, t); + tcg_temp_free_i64(t); +} + void tcg_gen_abs_i64(TCGv_i64 ret, TCGv_i64 a) { TCGv_i64 t = tcg_temp_ebb_new_i64(); From bab972a2b0e34b889fbe8deb1e41cbea47e797f2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 12:15:39 -0700 Subject: [PATCH 678/860] target/arm: Fix SVE2 WHILEWR/WHILERW zero diff boundary case The trans_WHILE_ptr function incorrectly handles the case where the address difference divided by ESIZE results in zero. This happens when the address difference is less than ESIZE but greater than zero. Fix by dropping direct comparisons of op0 vs op1, and instead testing the scaled diff vs 0. Merge with the bounding to the maximum vector length via wrapping arithmetic. Cc: qemu-stable@nongnu.org Fixes: 14f6dad168e ("target/arm: Implement SVE2 WHILERW, WHILEWR") Reported-by: YanjunYang Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260811191540.79882-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index fc4cc8c479..0becef16a9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3697,7 +3697,7 @@ TRANS_FEAT(WHILE_gt_cnt4, aa64_sme2_or_sve2p1, do_WHILE, static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) { - TCGv_i64 op0, op1, diff, t1, tmax; + TCGv_i64 op0, op1, diff, t1; TCGv_i32 t2; TCGv_ptr ptr; unsigned vsz = vec_full_reg_size(s); @@ -3713,7 +3713,6 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) op0 = read_cpu_reg(s, a->rn, 1); op1 = read_cpu_reg(s, a->rm, 1); - tmax = tcg_constant_i64(vsz >> a->esz); diff = tcg_temp_new_i64(); if (a->rw) { @@ -3723,25 +3722,36 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) tcg_gen_sub_i64(diff, op0, op1); tcg_gen_sub_i64(t1, op1, op0); tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, diff, t1); - /* Divide, rounding down, by ESIZE. */ - tcg_gen_shri_i64(diff, diff, a->esz); - /* If op1 == op0, diff == 0, and the condition is always true. */ - tcg_gen_movcond_i64(TCG_COND_EQ, diff, op0, op1, tmax, diff); } else { /* WHILEWR */ - tcg_gen_sub_i64(diff, op1, op0); - /* Divide, rounding down, by ESIZE. */ - tcg_gen_shri_i64(diff, diff, a->esz); - /* If op0 >= op1, diff <= 0, the condition is always true. */ - tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, tmax, diff); + /* Saturating subtraction maps diff <= 0 to diff == 0. */ + tcg_gen_ussub_i64(diff, op1, op0); } - /* Bound to the maximum. */ - tcg_gen_umin_i64(diff, diff, tmax); + /* Divide, rounding down, by ESIZE. */ + tcg_gen_shri_i64(diff, diff, a->esz); - /* Since we're bounded, pass as a 32-bit type. */ + /* + * If diff == 0, the condition is always true. Also, bound to max. + * Simplify + * diff = diff ? diff : max; + * diff = umin(diff, max); + * via + * diff -= 1; + * diff = umin(diff, max - 1); + * diff += 1; + * via 0 - 1 == UINT64_MAX. + */ + tcg_gen_addi_i64(diff, diff, -1); + tcg_gen_umin_i64(diff, diff, tcg_constant_i64((vsz >> a->esz) - 1)); + + /* + * Since we're bounded, pass as a 32-bit type. + * Sink the diff += 1 from above into the 32-bit type. + */ t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, diff); + tcg_gen_addi_i32(t2, t2, 1); desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); From 32953700108cc3305f27b591d66b5fb73806266a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Aug 2026 12:15:40 -0700 Subject: [PATCH 679/860] tests/tcg/aarch64: Add regression test for whilewr/whilerw Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20260811191540.79882-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- tests/tcg/aarch64/Makefile.target | 5 +++-- tests/tcg/aarch64/sve-while-ptr.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/tcg/aarch64/sve-while-ptr.c diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 32f2689273..6cc81bf854 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -203,8 +203,9 @@ endif endif ifneq ($(CROSS_CC_HAS_SVE2),) -AARCH64_TESTS += test-826 -test-826: CFLAGS += $(CROSS_CC_HAS_SVE2) +SVE2_TESTS = test-826 sve-while-ptr +$(SVE2_TESTS): CFLAGS += $(CROSS_CC_HAS_SVE2) +AARCH64_TESTS += $(SVE2_TESTS) endif TESTS += $(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/sve-while-ptr.c b/tests/tcg/aarch64/sve-while-ptr.c new file mode 100644 index 0000000000..7db3b9cc03 --- /dev/null +++ b/tests/tcg/aarch64/sve-while-ptr.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* WHILEWR / WHILERW regression test */ + +#include +#include + +int main(int argc, char **argv) +{ + unsigned short p; + int set_vl_ret; + + set_vl_ret = prctl(PR_SVE_SET_VL, 16, 0, 0, 0, 0); + assert(set_vl_ret == 16); + + p = 0xdead; + asm("whilewr p0.s, %0, %1\n\t" + "str p0, [%2]" + : : "r"(8), "r"(11), "r"(&p) : "memory", "p0"); + assert(p == 0x1111); + + p = 0xdead; + asm("whilerw p0.s, %0, %1\n\t" + "str p0, [%2]" + : : "r"(8), "r"(11), "r"(&p) : "memory", "p0"); + assert(p == 0x1111); + + return 0; +} From 4f50d637e23770872a997ed2de0729fbacc2c002 Mon Sep 17 00:00:00 2001 From: Doug Cook Date: Mon, 17 Aug 2026 19:09:06 +0000 Subject: [PATCH 680/860] hw/arm/ax3000-soc: fix heap overflow from missing class_size TYPE_AX3000_SOC declares an Ax3000SoCClass via OBJECT_DECLARE_TYPE() and ax3000_class_init() writes to it: Ax3000SoCClass *sc = AX3000_SOC_CLASS(oc); sc->num_cpus = AX3000_NUM_CPUS; but its TypeInfo omits .class_size, so type_initialize() only allocates class_size inherited from the parent, i.e. sizeof(SysBusDeviceClass). The store to sc->num_cpus therefore writes 4 bytes of the value 4 just past the end of the class allocation, corrupting whatever heap block follows it. Fix by setting class_size. Fixes: 33a71a68c6e1 ("hw/arm: Add Axiado SoC AX3000") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4197 Signed-off-by: Doug Cook Message-id: LVXPR21MB70090B04FF7397B0F2A201C8ADA72@LVXPR21MB7009.namprd21.prod.outlook.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/ax3000-soc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/ax3000-soc.c b/hw/arm/ax3000-soc.c index 71e31c6fb4..ebe174fb97 100644 --- a/hw/arm/ax3000-soc.c +++ b/hw/arm/ax3000-soc.c @@ -236,6 +236,7 @@ static const TypeInfo axiado_soc_types[] = { .instance_size = sizeof(Ax3000SoCState), .instance_init = ax3000_init, .class_init = ax3000_class_init, + .class_size = sizeof(Ax3000SoCClass), } }; From b3f00f492f8a9e6a750554cb6dcb21bdcfd80db9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:31 +0100 Subject: [PATCH 681/860] target/arm: Handle NSACR.NSASEDIS in CPACR accesses In cpacr_read() and cpacr_write() we have code that implements the "CPACR.{cp10,cp11} behave as RAZ/WI from NonSecure when NSACR.cp10 is 0" behaviour. There is a similar requirement for CPACR.ASEDIS: if NSACR.NSASEDIS is 1 then CPACR.ASEDIS behaves as RAO/WI in NS. This doesn't matter to us yet because we don't currently implement ASEDIS or NSASEDIS. But we're about to do that, so add the handling to cpacr_read() and cpacr_write(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-2-peter.maydell@linaro.org --- target/arm/helper.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index adae2b2b8e..6a707b80cf 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -588,11 +588,19 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* * For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10 * is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00. + * Similarly, if NSACR.NSASEDIS is 1 then CPACR.ASEDIS ignores writes + * and reads as 1. */ if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - mask = R_CPACR_CP11_MASK | R_CPACR_CP10_MASK; - value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + mask = R_CPACR_CP11_MASK | R_CPACR_CP10_MASK; + value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + mask = R_CPACR_ASEDIS_MASK; + value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + } } env->cp15.cpacr_el1 = value; @@ -603,12 +611,18 @@ static uint64_t cpacr_read(CPUARMState *env, const ARMCPRegInfo *ri) /* * For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10 * is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00. + * Similarly NSACR.NSASEDIS makes CPACR.ASEDIS read as 1. */ uint64_t value = env->cp15.cpacr_el1; if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - value = ~(R_CPACR_CP11_MASK | R_CPACR_CP10_MASK); + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + value = ~(R_CPACR_CP11_MASK | R_CPACR_CP10_MASK); + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + value |= R_CPACR_ASEDIS_MASK; + } } return value; } From 653311eecdcbeae672bace9039b8c65745d1693c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:32 +0100 Subject: [PATCH 682/860] target/arm: Handle NSACR.NSASEDIS in HCPTR accesses In cptr_el2_read() and cptr_el2_write() we have code that implements the "HCPTR.{TCP10,TCP11} behave as RAO/WI from NonSecure when NSACR.cp10 is 0" behaviour. There is a similar requirement for HCPTR.TASE: if NSACR.NSASEDIS is 1 then CPACR.ASEDIS behaves as RAO/WI in NS. This doesn't matter to us yet because we don't currently implement ASEDIS or NSASEDIS. But we're about to do that, so add the handling to cptr_el2_read() and cptr_el2_write(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-3-peter.maydell@linaro.org --- target/arm/helper.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 6a707b80cf..d582eeba57 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4117,11 +4117,18 @@ static void cptr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, /* * For A-profile AArch32 EL3, if NSACR.CP10 * is 0 then HCPTR.{TCP11,TCP10} ignore writes and read as 1. + * Similarly, if NSACR.NSASEDIS is 1 then HCPTR.TASE behaves as RAO/WI. */ if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - uint64_t mask = R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; - value = (value & ~mask) | (env->cp15.cptr_el[2] & mask); + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + uint64_t mask = R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; + value = (value & ~mask) | (env->cp15.cptr_el[2] & mask); + } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + uint64_t mask = R_HCPTR_TASE_MASK; + value = (value & ~mask) | (env->cp15.cptr_el[2] & mask); + } } env->cp15.cptr_el[2] = value; } @@ -4131,12 +4138,18 @@ static uint64_t cptr_el2_read(CPUARMState *env, const ARMCPRegInfo *ri) /* * For A-profile AArch32 EL3, if NSACR.CP10 * is 0 then HCPTR.{TCP11,TCP10} ignore writes and read as 1. + * Similarly, if NSACR.NSASEDIS is 1 then HCPTR.TASE behaves as RAO/WI. */ uint64_t value = env->cp15.cptr_el[2]; if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && - !arm_is_secure(env) && !FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { - value |= R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; + !arm_is_secure(env)) { + if (!FIELD_EX32(env->cp15.nsacr, NSACR, CP10)) { + value |= R_HCPTR_TCP11_MASK | R_HCPTR_TCP10_MASK; + } + if (!FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + value |= R_HCPTR_TASE_MASK; + } } return value; } From 11dacc5b839bd5661153b5855b423c4098c0e124 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:33 +0100 Subject: [PATCH 683/860] target/arm: Update confusing comment in cpacr_write() In cpacr_write() a comment says "In ARMv8 most bits of CPACR_EL1 are RES0", and the mask value is left at 0, implying that we enforce those RES0 bits. In fact we only enforce RES0 when ARM_FEATURE_V8 is not implemented, and for v8 and up we allow the guest to write any bits. The addition of architectural features in v8 and v9 has also resulted in the addition of new bits to CPACR_EL1, so "most bits are RES0" isn't really true any more. Update the comment to be a bit clearer. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-4-peter.maydell@linaro.org --- target/arm/helper.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index d582eeba57..7de38d8201 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -554,7 +554,17 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, { uint32_t mask = 0; - /* In ARMv8 most bits of CPACR_EL1 are RES0. */ + /* + * The AArch64 view of CPACR_EL1 has a different layout to the old + * AArch32 one. We also need to permit the old AArch32 bits to be + * read and written so that an AArch64 EL2 hypervisor can set up + * the register for an AArch32 EL1 guest. So we choose not to + * enforce any RAZ/WI or RAO/WI bits for v8 based on feature + * presence/absence. + * + * For v7 the situation is a bit simpler and there we do choose to + * enforce RAZ/WI and RAO/WI. + */ if (!arm_feature(env, ARM_FEATURE_V8)) { /* * ARMv7 defines bits for unimplemented coprocessors as RAZ/WI. From fdf3c577a966e06e03ede48f17fa905f1ee07c40 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:34 +0100 Subject: [PATCH 684/860] target/arm: Add new feature ARM_FEATURE_NEON_TRAPS The traps configurable via CPACR.ASEDIS and HCPTR.TASE that trap only Neon instructions are unfortunately IMPDEF about whether they are implemented or not, and there is no ID register field that identifies whether they are present. In practice, they are present on every implementation I have checked except for the Cortex-A8 (which was the first CPU with Neon). Add a new feature ARM_FEATURE_NEON_TRAPS which we set on every ARM_FEATURE_NEON CPU except the Cortex-A8, and make the CPACR.ASEDIS and HCPTR.TASE bits RAZ/WI unless the CPU has ARM_FEATURE_NEON and ARM_FEATURE_NEON_TRAPS. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-5-peter.maydell@linaro.org --- target/arm/cpu-max.c | 1 + target/arm/cpu.h | 7 +++++++ target/arm/cpu64.c | 1 + target/arm/helper.c | 17 +++++++++++++++-- target/arm/tcg/cpu32.c | 8 ++++++++ target/arm/tcg/cpu64.c | 10 ++++++++++ 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu-max.c b/target/arm/cpu-max.c index 8d968336a8..4df52bcb08 100644 --- a/target/arm/cpu-max.c +++ b/target/arm/cpu-max.c @@ -23,6 +23,7 @@ void aarch64_aa32_a57_init(ARMCPU *cpu, bool aarch64_enabled) cpu->dtb_compatible = "arm,cortex-a57"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); if (aarch64_enabled) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c0492c8dfb..4f07332543 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2177,6 +2177,13 @@ enum arm_features { * CPU types added in future. */ ARM_FEATURE_BACKCOMPAT_CNTFRQ, /* 62.5MHz timer default */ + /* + * ARM_FEATURE_NEON_TRAPS should be set if the CPU implements the + * CPACR.ASEDIS and HCPTR.TASE bits for trapping A32 Neon. This + * is architecturally IMPDEF, but seems to be implemented by all + * ARM_FEATURE_NEON CPUs except the Cortex-A8. + */ + ARM_FEATURE_NEON_TRAPS, }; static inline int arm_feature(const CPUARMState *env, int feature) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index faaf3ab879..4e8c472530 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -699,6 +699,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a53"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); diff --git a/target/arm/helper.c b/target/arm/helper.c index 7de38d8201..2dfbeae0ef 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -573,14 +573,20 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, */ if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { /* VFP coprocessor: cp10 & cp11 [23:20] */ - mask |= R_CPACR_ASEDIS_MASK | - R_CPACR_D32DIS_MASK | + mask |= R_CPACR_D32DIS_MASK | R_CPACR_CP11_MASK | R_CPACR_CP10_MASK; if (!arm_feature(env, ARM_FEATURE_NEON)) { /* ASEDIS [31] bit is RAO/WI */ value |= R_CPACR_ASEDIS_MASK; + mask |= R_CPACR_ASEDIS_MASK; + } else if (arm_feature(env, ARM_FEATURE_NEON_TRAPS)) { + /* + * bit is present unless CPU doesn't implement ASEDIS + * (in which case it is RAZ/WI; this is the Cortex-A8) + */ + mask |= R_CPACR_ASEDIS_MASK; } /* @@ -4124,6 +4130,13 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) static void cptr_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + if (!arm_feature(env, ARM_FEATURE_NEON_TRAPS)) { + /* + * If CPU doesn't implement HCPTR.TASE it's RAZ/WI. Note that + * NSACR.NSASEDIS being 1 overrides this. + */ + value &= ~R_HCPTR_TASE_MASK; + } /* * For A-profile AArch32 EL3, if NSACR.CP10 * is 0 then HCPTR.{TCP11,TCP10} ignore writes and read as 1. diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index d5dd236152..724a954934 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -251,6 +251,10 @@ static void cortex_a8_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a8"; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_NEON); + /* + * The Cortex-A8 doesn't have CPACR.ASEDIS and HCPTR.TASE, + * so don't set ARM_FEATURE_NEON_TRAPS. + */ set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_EL3); @@ -321,6 +325,7 @@ static void cortex_a9_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a9"; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); @@ -389,6 +394,7 @@ static void cortex_a7_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a7"; set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); @@ -439,6 +445,7 @@ static void cortex_a15_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a15"; set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); @@ -634,6 +641,7 @@ static void cortex_r52_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_PMSA); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 9ce514dda8..b38c61ba62 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -37,6 +37,7 @@ static void aarch64_a35_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a35"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -208,6 +209,7 @@ static void aarch64_a55_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a55"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -281,6 +283,7 @@ static void aarch64_a72_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a72"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -341,6 +344,7 @@ static void aarch64_a76_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a76"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -415,6 +419,7 @@ static void aarch64_a78ae_initfn(Object *obj) 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_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -488,6 +493,7 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->dtb_compatible = "arm,a64fx"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -662,6 +668,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->dtb_compatible = "arm,neoverse-n1"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -738,6 +745,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->dtb_compatible = "arm,neoverse-v1"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -965,6 +973,7 @@ static void aarch64_a710_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a710"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); @@ -1067,6 +1076,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->dtb_compatible = "arm,neoverse-n2"; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_NEON_TRAPS); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_BACKCOMPAT_CNTFRQ); set_feature(&cpu->env, ARM_FEATURE_AARCH64); From 284a3c29246097ce8d0afd8676371c97cd796188 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:35 +0100 Subject: [PATCH 685/860] target/arm: Implement CPACR.ASEDIS and HCPTR.TASE When executing at AArch32, there are optional trap bits for Neon instructions in CPACR and HCPTR. We don't currently implement these. Now we have a separate code path for access checks for Neon insns, we can straightforwardly add the check there. We need to track the target EL for Neon-specific trapping in a new TB flag. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/1499 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-6-peter.maydell@linaro.org --- target/arm/cpu.h | 6 +++ target/arm/tcg/hflags.c | 82 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-vfp.c | 16 +++++-- target/arm/tcg/translate.c | 1 + target/arm/tcg/translate.h | 1 + 5 files changed, 103 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 4f07332543..afc3fda3b8 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2506,6 +2506,12 @@ FIELD(TBFLAG_A32, NS, 10, 1) * This requires an SME trap from AArch32 mode when using NEON. */ FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1) +/* + * Target EL for a Neon-disabled exception via CPACR.ASEDIS, HCPTR.TASE. + * If FPEXC_EL indicates a trap to a lower EL than this, that will + * take precedence. + */ +FIELD(TBFLAG_A32, NEONEXC_EL, 12, 2) /* * Bit usage when in AArch32 state, for M-profile only. diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 0716ca98fd..14824e3dff 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -164,6 +164,86 @@ static bool sme_fa64(CPUARMState *env, int el) return true; } +static int neon_exception_el(CPUARMState *env, int cur_el) +{ + /* + * Return the EL to trap to for A32 Neon specific traps + * (CPACR.ASEDIS and HCPTR.TASE). In the pseudocode these are + * checked in the same function as the more general trap bits that + * we handle in fp_exception_el(). Fortunately it is always the + * case that if the trap/enable bits specify taking an exception + * to different ELs for the Neon-specific insns and the general fp + * insns then the trap to the lower of the two ELs has priority, + * so we can calculate the two target ELs separately and pick the + * right destination later. Compare AArch32_CheckAdvSIMDOrFPEnabled(). + * + * CPACR doesn't exist before v6, but neither does Neon, so we can + * assume that if we're here testing this then the register exists. + * HCPTR always exists if EL2 is present. + */ + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool cpacr_asedis = FIELD_EX64(env->cp15.cpacr_el1, CPACR, ASEDIS); + bool hcptr_tase = FIELD_EX64(env->cp15.cptr_el[2], HCPTR, TASE); + bool have_aarch32_el3 = + arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); + + if (!arm_feature(env, ARM_FEATURE_NEON_TRAPS)) { + /* This CPU doesn't implement the trap bits (Cortex-A8) */ + return 0; + } + + if (arm_feature(env, ARM_FEATURE_EL2) && arm_el_is_aa64(env, 2)) { + /* + * The AArch64 CPTR_EL2 has no equivalent to HCPTR.TASE; only + * an AArch32 EL2 can trap Neon specifically. + */ + hcptr_tase = false; + } + + /* + * We know we're in AArch32, but if this is EL0 and EL1 is AArch64 + * then CPACR_EL1 applies rather than CPACR, and it doesn't have + * ASEDIS (instead using the same bit for TCPAC). + */ + if (cur_el == 0 && arm_el_is_aa64(env, 1)) { + cpacr_asedis = false; + } + + /* CPACR is ignored if E2H+TGE are both set */ + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE)) { + cpacr_asedis = false; + } + + /* + * NSACR.NSASEDIS makes the effective values of HCPTR.TASE and + * CPACR.ASEDIS be 1 in NonSecure state. NSACR has no + * effect unless EL3 exists and is AArch32. + */ + if (have_aarch32_el3 && cur_el <= 2 && !arm_is_secure_below_el3(env)) { + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { + cpacr_asedis = true; + hcptr_tase = true; + } + } + + if (cpacr_asedis) { + if (have_aarch32_el3 && (cur_el == 3 || arm_is_secure_below_el3(env))) { + /* Trap from Secure PL0 or PL1 to Secure PL1 */ + return 3; + } + if (cur_el <= 1) { + /* trap from EL0 or EL1 to EL1 */ + return 1; + } + } + + /* HCPTR.TASE traps to EL2, including for execution at EL2 */ + if (hcptr_tase && cur_el <= 2) { + return 2; + } + return 0; +} + static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, ARMMMUIdx mmu_idx) { @@ -209,6 +289,8 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_A32(flags, SME_TRAP_NONSTREAMING, 1); } + DP_TBFLAG_A32(flags, NEONEXC_EL, neon_exception_el(env, el)); + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 6e944a0322..4db4b14841 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -306,15 +306,25 @@ bool vfp_access_check(DisasContext *s) /* * Access check for Neon; this is for instructions which can be - * trapped by CPACR.ASEDIS and HCPTR.TASE. Support for those traps - * is optional and we currently do not implement them, so this - * is identical to a VFP access check for now. + * trapped by CPACR.ASEDIS and HCPTR.TASE. */ bool neon_access_check(DisasContext *s) { if (arm_dc_feature(s, ARM_FEATURE_M)) { return vfp_access_check_m(s, false); } else { + /* + * If the Neon-specific trap bits request a trap to a lower EL + * than the general FP trap bits, the trap to the lower EL + * has priority. + */ + if (s->neon_excp_el && + (!s->fp_excp_el || s->neon_excp_el < s->fp_excp_el)) { + uint32_t syn = syn_a32_fp_access_trap(1, 0xe, 1, 0); + + gen_exception_insn_el(s, 0, EXCP_UDEF, syn, s->neon_excp_el); + return false; + } return vfp_access_check_a(s, false, true); } } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 1770428d3c..7306a1389c 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -6403,6 +6403,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); + dc->neon_excp_el = EX_TBFLAG_A32(tb_flags, NEONEXC_EL); } dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index a3d03159ad..cce84f29c4 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -88,6 +88,7 @@ typedef struct DisasContext { int sve_excp_el; /* SVE exception EL or 0 if enabled */ int sme_excp_el; /* SME exception EL or 0 if enabled */ int zt0_excp_el; /* ZT0 exception EL or 0 if enabled */ + int neon_excp_el; /* A32 Neon exception EL or 0 if enabled */ int vl; /* current vector length in bytes */ int svl; /* current streaming vector length in bytes */ int max_svl; /* maximum implemented streaming vector length */ From 975a1bd4399b0a00f89996e838f418c256fa0b56 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:36 +0100 Subject: [PATCH 686/860] target/arm: Pull VFP dregs checks out into a function Currently we directly call dc_isar_feature(aa32_simd_r32, s) for VFP insns that use D16-D31 to see if they should UNDEF. For some v7A CPUs (Cortex-A7, Cortex-A9) there is also a CPACR.D32DIS trap bit that will make VFP (and only VFP, not Neon) insns using D16-D31 UNDEF. Abstract out the register check for VFP insns into a new function which will provide us a place where we can make this check. Since D32DIS takes precedence over traps to EL2 and EL3 and simply makes the insns UNDEF, we are OK to check it at the same point when we do the CPU feature check, rather than having to wait until vfp_access_check(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-7-peter.maydell@linaro.org --- target/arm/tcg/translate-vfp.c | 70 ++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index 4db4b14841..a8d470ec49 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -207,6 +207,23 @@ static void gen_update_fp_context(DisasContext *s) } } +/* + * Return true if a VFP insn is OK to access the registers indicated + * by regmask, false if it should UNDEF. This checks whether the + * D16-D31 regs are implemented by the CPU. Eventually we will also check + * CPACR.D32DIS. + * Note that Neon insns accessing D16..D31 do not need to check D32DIS, + * so this function is for VFP insns only. + * + * @regmask should be the logical OR of the VFP Dregs being accessed. + */ +static bool vfp_dregs_ok(DisasContext *s, int dregmask) +{ + int invalid_dreg_mask = dc_isar_feature(aa32_simd_r32, s) ? 0 : 0x10; + + return !(dregmask & invalid_dreg_mask); +} + /* * Check that VFP access is enabled, A-profile specific version. * @@ -347,8 +364,7 @@ static bool trans_VSEL(DisasContext *s, arg_VSEL *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (sz == 3 && !dc_isar_feature(aa32_simd_r32, s) && - ((a->vm | a->vn | a->vd) & 0x10)) { + if (sz == 3 && !vfp_dregs_ok(s, a->vm | a->vn | a->vd)) { return false; } @@ -473,8 +489,7 @@ static bool trans_VRINT(DisasContext *s, arg_VRINT *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (sz == 3 && !dc_isar_feature(aa32_simd_r32, s) && - ((a->vm | a->vd) & 0x10)) { + if (sz == 3 && !vfp_dregs_ok(s, a->vm | a->vd)) { return false; } @@ -541,7 +556,7 @@ static bool trans_VCVT(DisasContext *s, arg_VCVT *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (sz == 3 && !dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (sz == 3 && !vfp_dregs_ok(s, a->vm)) { return false; } @@ -662,7 +677,7 @@ static bool trans_VMOV_to_gp(DisasContext *s, arg_VMOV_to_gp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { + if (!vfp_dregs_ok(s, a->vn & 0x10)) { return false; } @@ -709,7 +724,7 @@ static bool trans_VMOV_from_gp(DisasContext *s, arg_VMOV_from_gp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { + if (!vfp_dregs_ok(s, a->vn & 0x10)) { return false; } @@ -745,7 +760,7 @@ static bool trans_VDUP(DisasContext *s, arg_VDUP *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vn & 0x10)) { + if (!vfp_dregs_ok(s, a->vn)) { return false; } @@ -1030,7 +1045,7 @@ static bool trans_VMOV_64_dp(DisasContext *s, arg_VMOV_64_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -1132,7 +1147,7 @@ static bool trans_VLDR_VSTR_dp(DisasContext *s, arg_VLDR_VSTR_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -1261,7 +1276,7 @@ static bool trans_VLDM_VSTM_dp(DisasContext *s, arg_VLDM_VSTM_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd + n) > 16) { + if (!vfp_dregs_ok(s, a->vd + n - 1)) { return false; } @@ -1513,7 +1528,7 @@ static bool do_vfp_3op_dp(DisasContext *s, VFPGen3OpDPFn *fn, } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((vd | vn | vm) & 0x10)) { + if (!vfp_dregs_ok(s, vd | vn | vm)) { return false; } @@ -1685,7 +1700,7 @@ static bool do_vfp_2op_dp(DisasContext *s, VFPGen2OpDPFn *fn, int vd, int vm) /* Note that the caller must check the aa32_fpdp_v2 feature. */ /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((vd | vm) & 0x10)) { + if (!vfp_dregs_ok(s, vd | vm)) { return false; } @@ -2252,8 +2267,7 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2380,7 +2394,7 @@ static bool trans_VMOV_imm_dp(DisasContext *s, arg_VMOV_imm_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (vd & 0x10)) { + if (!vfp_dregs_ok(s, vd)) { return false; } @@ -2555,7 +2569,7 @@ static bool trans_VCMP_dp(DisasContext *s, arg_VCMP_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2621,7 +2635,7 @@ static bool trans_VCVT_f64_f16(DisasContext *s, arg_VCVT_f64_f16 *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -2702,7 +2716,7 @@ static bool trans_VCVT_f16_f64(DisasContext *s, arg_VCVT_f16_f64 *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -2777,7 +2791,7 @@ static bool trans_VRINTR_dp(DisasContext *s, arg_VRINTR_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2856,7 +2870,7 @@ static bool trans_VRINTZ_dp(DisasContext *s, arg_VRINTZ_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2930,7 +2944,7 @@ static bool trans_VRINTX_dp(DisasContext *s, arg_VRINTX_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && ((a->vd | a->vm) & 0x10)) { + if (!vfp_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2956,7 +2970,7 @@ static bool trans_VCVT_sp(DisasContext *s, arg_VCVT_sp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -2982,7 +2996,7 @@ static bool trans_VCVT_dp(DisasContext *s, arg_VCVT_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -3063,7 +3077,7 @@ static bool trans_VCVT_int_dp(DisasContext *s, arg_VCVT_int_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -3100,7 +3114,7 @@ static bool trans_VJCVT(DisasContext *s, arg_VJCVT *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } @@ -3240,7 +3254,7 @@ static bool trans_VCVT_fix_dp(DisasContext *s, arg_VCVT_fix_dp *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!vfp_dregs_ok(s, a->vd)) { return false; } @@ -3369,7 +3383,7 @@ static bool trans_VCVT_dp_int(DisasContext *s, arg_VCVT_dp_int *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vm & 0x10)) { + if (!vfp_dregs_ok(s, a->vm)) { return false; } From 282ff9baebba548f8b6bfb63ffd53f40c8a0ce14 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:37 +0100 Subject: [PATCH 687/860] target/arm: Implement CPACR.D32DIS On some v7A CPUs, CPACR.D32DIS is a bit allowing the guest to make VFP instructions that touch registers D16..D31 UNDEF. Whether the CPU implements this or not is IMPDEF, and the only two CPUs we implement which have this are the Cortex-A7 and Cortex-A9. In v8A the bit is no longer defined at all. Since the only kind of trapping that needs to be done is a simple UNDEF, this is straightforward enough to implement. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/1499 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-8-peter.maydell@linaro.org --- target/arm/cpu.h | 4 ++++ target/arm/helper.c | 19 ++++++++++++++++--- target/arm/tcg/cpu32.c | 2 ++ target/arm/tcg/hflags.c | 20 ++++++++++++++++++++ target/arm/tcg/translate-vfp.c | 7 ++----- target/arm/tcg/translate.c | 6 ++++++ target/arm/tcg/translate.h | 1 + 7 files changed, 51 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index afc3fda3b8..e8dfc3179f 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2184,6 +2184,8 @@ enum arm_features { * ARM_FEATURE_NEON CPUs except the Cortex-A8. */ ARM_FEATURE_NEON_TRAPS, + /* Does the CPU implement CPACR.D32DIS ? */ + ARM_FEATURE_D32DIS, }; static inline int arm_feature(const CPUARMState *env, int feature) @@ -2512,6 +2514,8 @@ FIELD(TBFLAG_A32, SME_TRAP_NONSTREAMING, 11, 1) * take precedence. */ FIELD(TBFLAG_A32, NEONEXC_EL, 12, 2) +/* Should VFP insns touching D16..D31 UNDEF? (CPACR.D32DIS) */ +FIELD(TBFLAG_A32, D32DIS, 14, 1) /* * Bit usage when in AArch32 state, for M-profile only. diff --git a/target/arm/helper.c b/target/arm/helper.c index 2dfbeae0ef..4f30a94ecd 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -573,8 +573,7 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, */ if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { /* VFP coprocessor: cp10 & cp11 [23:20] */ - mask |= R_CPACR_D32DIS_MASK | - R_CPACR_CP11_MASK | + mask |= R_CPACR_CP11_MASK | R_CPACR_CP10_MASK; if (!arm_feature(env, ARM_FEATURE_NEON)) { @@ -596,6 +595,13 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, if (!cpu_isar_feature(aa32_simd_r32, env_archcpu(env))) { /* D32DIS [30] is RAO/WI if D16-31 are not implemented. */ value |= R_CPACR_D32DIS_MASK; + mask |= R_CPACR_D32DIS_MASK; + } else if (arm_feature(env, ARM_FEATURE_D32DIS)) { + /* + * Bit is present unless CPU doesn't implement D32DIS, + * in which case it is RAZ/WI. + */ + mask |= R_CPACR_D32DIS_MASK; } } value &= mask; @@ -605,7 +611,7 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, * For A-profile AArch32 EL3 (but not M-profile secure mode), if NSACR.CP10 * is 0 then CPACR.{CP11,CP10} ignore writes and read as 0b00. * Similarly, if NSACR.NSASEDIS is 1 then CPACR.ASEDIS ignores writes - * and reads as 1. + * and reads as 1, and NSACR.NSD32DIS makes CPACR.D32DIS behave as RAO/WI. */ if (arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && !arm_is_secure(env)) { @@ -617,6 +623,10 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, mask = R_CPACR_ASEDIS_MASK; value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) { + mask = R_CPACR_D32DIS_MASK; + value = (value & ~mask) | (env->cp15.cpacr_el1 & mask); + } } env->cp15.cpacr_el1 = value; @@ -639,6 +649,9 @@ static uint64_t cpacr_read(CPUARMState *env, const ARMCPRegInfo *ri) if (FIELD_EX32(env->cp15.nsacr, NSACR, NSASEDIS)) { value |= R_CPACR_ASEDIS_MASK; } + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) { + value |= R_CPACR_D32DIS_MASK; + } } return value; } diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 724a954934..bc07e33877 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -336,6 +336,7 @@ static void cortex_a9_initfn(Object *obj) */ set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_CBAR); + set_feature(&cpu->env, ARM_FEATURE_D32DIS); cpu->midr = 0x410fc090; cpu->reset_fpsid = 0x41033090; cpu->isar.mvfr0 = 0x11110222; @@ -403,6 +404,7 @@ static void cortex_a7_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); + set_feature(&cpu->env, ARM_FEATURE_D32DIS); cpu->midr = 0x410fc075; cpu->reset_fpsid = 0x41023075; cpu->isar.mvfr0 = 0x10110222; diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 14824e3dff..296ec8101a 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -244,6 +244,24 @@ static int neon_exception_el(CPUARMState *env, int cur_el) return 0; } +static bool arm_d32dis(CPUARMState *env, int cur_el) +{ + bool cpacr_d32dis = FIELD_EX64(env->cp15.cpacr_el1, CPACR, D32DIS); + + if (!arm_feature(env, ARM_FEATURE_D32DIS)) { + return false; + } + + /* If NSACR.NSD32DIS is set, CPACR.D32DIS acts as 1 in NonSecure */ + if ((arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3) && + cur_el <= 2 && !arm_is_secure_below_el3(env))) { + if (FIELD_EX32(env->cp15.nsacr, NSACR, NSD32DIS)) { + cpacr_d32dis = true; + } + } + return cpacr_d32dis; +} + static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, ARMMMUIdx mmu_idx) { @@ -291,6 +309,8 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, DP_TBFLAG_A32(flags, NEONEXC_EL, neon_exception_el(env, el)); + DP_TBFLAG_A32(flags, D32DIS, arm_d32dis(env, el)); + return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index a8d470ec49..0a87a0165b 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -210,8 +210,7 @@ static void gen_update_fp_context(DisasContext *s) /* * Return true if a VFP insn is OK to access the registers indicated * by regmask, false if it should UNDEF. This checks whether the - * D16-D31 regs are implemented by the CPU. Eventually we will also check - * CPACR.D32DIS. + * D16-D31 regs are implemented by the CPU and not disabled by CPACR.D32DIS. * Note that Neon insns accessing D16..D31 do not need to check D32DIS, * so this function is for VFP insns only. * @@ -219,9 +218,7 @@ static void gen_update_fp_context(DisasContext *s) */ static bool vfp_dregs_ok(DisasContext *s, int dregmask) { - int invalid_dreg_mask = dc_isar_feature(aa32_simd_r32, s) ? 0 : 0x10; - - return !(dregmask & invalid_dreg_mask); + return !(dregmask & s->invalid_vfp_dreg_mask); } /* diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 7306a1389c..90eb6f3005 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -6342,6 +6342,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ARMCPU *cpu = env_archcpu(env); CPUARMTBFlags tb_flags = arm_tbflags_from_tb(dc->base.tb); uint32_t condexec, core_mmu_idx; + bool d32dis = false; dc->isar = &cpu->isar; dc->condjmp = 0; @@ -6404,7 +6405,12 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); dc->neon_excp_el = EX_TBFLAG_A32(tb_flags, NEONEXC_EL); + d32dis = EX_TBFLAG_A32(tb_flags, D32DIS); } + + dc->invalid_vfp_dreg_mask = + (d32dis || !dc_isar_feature(aa32_simd_r32, dc)) ? 0x10 : 0; + dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; dc->features = env->features; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index cce84f29c4..b08bded398 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -94,6 +94,7 @@ typedef struct DisasContext { int max_svl; /* maximum implemented streaming vector length */ int max_any_vl; /* maximum implemented vector length */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ + int invalid_vfp_dreg_mask; /* mask for whether VFP D16..D31 should UNDEF */ int vec_len; int vec_stride; bool v7m_handler_mode; From 44d5b628fbce1612f3d8529699905e8dc9f57b1b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Aug 2026 13:38:38 +0100 Subject: [PATCH 688/860] target/arm: Pull Neon dregs checks out into a function Abstract out the register check for Neon insns into a new function, similarly to what we have for VFP. We don't have any extra checks that we need to add here, but having a neon_dregs_ok() is cleaner and means the Neon decode isn't oddly different to the VFP decode. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20260817123838.1578060-9-peter.maydell@linaro.org --- target/arm/tcg/translate-neon.c | 118 ++++++++++++++------------------ target/arm/tcg/translate.c | 1 + target/arm/tcg/translate.h | 1 + 3 files changed, 53 insertions(+), 67 deletions(-) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 50a44511d9..f811940f64 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -118,11 +118,24 @@ static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var) } } +/* + * Return true if a Neon insn is OK to access the registers indicated + * by regmask, false if it should UNDEF. This checks whether the + * D16-D31 regs are implemented by the CPU. + * Note that Neon insns accessing D16..D31 do not need to check D32DIS. + * + * @regmask should be the logical OR of the Dregs being accessed. + */ +static bool neon_dregs_ok(DisasContext *s, int dregmask) +{ + return !(dregmask & s->invalid_neon_dreg_mask); +} + static bool do_neon_ddda(DisasContext *s, int q, int vd, int vn, int vm, int data, gen_helper_gvec_4 *fn_gvec) { /* UNDEF accesses to D16-D31 if they don't exist. */ - if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + if (!neon_dregs_ok(s, vd | vn | vm)) { return false; } @@ -152,7 +165,7 @@ static bool do_neon_ddda_env(DisasContext *s, int q, int vd, int vn, int vm, int data, gen_helper_gvec_4_ptr *fn_gvec) { /* UNDEF accesses to D16-D31 if they don't exist. */ - if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + if (!neon_dregs_ok(s, vd | vn | vm)) { return false; } @@ -184,7 +197,7 @@ static bool do_neon_ddda_fpst(DisasContext *s, int q, int vd, int vn, int vm, gen_helper_gvec_4_ptr *fn_gvec_ptr) { /* UNDEF accesses to D16-D31 if they don't exist. */ - if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + if (!neon_dregs_ok(s, vd | vn | vm)) { return false; } @@ -240,8 +253,7 @@ static bool trans_VCADD(DisasContext *s, arg_VCADD *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -310,8 +322,7 @@ static bool trans_VFML(DisasContext *s, arg_VFML *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -398,14 +409,14 @@ static bool trans_VDOT_b16_scal(DisasContext *s, arg_VDOT_b16_scal *a) static bool trans_VFML_scalar(DisasContext *s, arg_VFML_scalar *a) { int opr_sz; + int dregmask = a->vd | (a->q ? a->vn : 0); if (!dc_isar_feature(aa32_fhm, s)) { return false; } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd & 0x10) || (a->q && (a->vn & 0x10)))) { + if (!neon_dregs_ok(s, dregmask)) { return false; } @@ -478,7 +489,7 @@ static bool trans_VLDST_multiple(DisasContext *s, arg_VLDST_multiple *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } if (a->itype > 10) { @@ -580,7 +591,7 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -672,7 +683,7 @@ static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a) } /* UNDEF accesses to D16-D31 if they don't exist */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -789,8 +800,7 @@ static bool do_3same(DisasContext *s, arg_3same *a, GVecGen3Fn fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1067,8 +1077,7 @@ static bool do_vector_2sh(DisasContext *s, arg_2reg_shift *a, GVecGen2iFn *fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1117,8 +1126,7 @@ static bool do_2shift_narrow_64(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1168,8 +1176,7 @@ static bool do_2shift_narrow_32(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1293,8 +1300,7 @@ static bool do_vshll_2sh(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1383,8 +1389,7 @@ static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -1428,7 +1433,7 @@ static bool do_1reg_imm(DisasContext *s, arg_1reg_imm *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) { + if (!neon_dregs_ok(s, a->vd)) { return false; } @@ -1485,8 +1490,7 @@ static bool do_prewiden_3d(DisasContext *s, arg_3diff *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1592,8 +1596,7 @@ static bool do_narrow_3d(DisasContext *s, arg_3diff *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1682,8 +1685,7 @@ static bool do_long_3d(DisasContext *s, arg_3diff *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -1944,8 +1946,7 @@ static bool trans_VMULL_P_3d(DisasContext *s, arg_3diff *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2027,8 +2028,7 @@ static bool do_2scalar(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2125,8 +2125,7 @@ static bool do_2scalar_fp_vec(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2222,8 +2221,7 @@ static bool do_vqrdmlah_2sc(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2293,8 +2291,7 @@ static bool do_2scalar_long(DisasContext *s, arg_2scalar *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2438,8 +2435,7 @@ static bool trans_VEXT(DisasContext *s, arg_VEXT *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2507,8 +2503,7 @@ static bool trans_VTBL(DisasContext *s, arg_VTBL *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vn | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vn | a->vm)) { return false; } @@ -2546,8 +2541,7 @@ static bool trans_VDUP_scalar(DisasContext *s, arg_VDUP_scalar *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2577,8 +2571,7 @@ static bool do_zip_uzp(DisasContext *s, arg_2misc *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2647,8 +2640,7 @@ static bool do_vmovn(DisasContext *s, arg_2misc *a, } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2711,8 +2703,7 @@ static bool trans_VSHLL(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2755,8 +2746,7 @@ static bool trans_VCVT_B16_F32(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2795,8 +2785,7 @@ static bool trans_VCVT_F16_F32(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2841,8 +2830,7 @@ static bool trans_VCVT_F32_F16(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -2887,8 +2875,7 @@ static bool do_2misc_vec(DisasContext *s, arg_2misc *a, GVecGen2Fn *fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -3015,8 +3002,7 @@ static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -3219,8 +3205,7 @@ static bool trans_VSWP(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } @@ -3292,8 +3277,7 @@ static bool trans_VTRN(DisasContext *s, arg_2misc *a) } /* UNDEF accesses to D16-D31 if they don't exist. */ - if (!dc_isar_feature(aa32_simd_r32, s) && - ((a->vd | a->vm) & 0x10)) { + if (!neon_dregs_ok(s, a->vd | a->vm)) { return false; } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 90eb6f3005..c866148383 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -6410,6 +6410,7 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->invalid_vfp_dreg_mask = (d32dis || !dc_isar_feature(aa32_simd_r32, dc)) ? 0x10 : 0; + dc->invalid_neon_dreg_mask = !dc_isar_feature(aa32_simd_r32, dc) ? 0x10 : 0; dc->lse2 = false; /* applies only to aarch64 */ dc->cp_regs = cpu->cp_regs; diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index b08bded398..31f52a34a6 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -95,6 +95,7 @@ typedef struct DisasContext { int max_any_vl; /* maximum implemented vector length */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ int invalid_vfp_dreg_mask; /* mask for whether VFP D16..D31 should UNDEF */ + int invalid_neon_dreg_mask; /* ditto, for Neon */ int vec_len; int vec_stride; bool v7m_handler_mode; From 7a0c61ad4ed53fe84e82283369728629ae9ea380 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:31 +0800 Subject: [PATCH 689/860] hw/arm: aspeed: Store boot info in the machine state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move the boot info into the existing AspeedMachineState. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260816131300.51799-2-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/aspeed.c | 17 +++++++---------- include/hw/arm/aspeed.h | 2 ++ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index a9238e6217..8fa16b3831 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -24,10 +24,6 @@ #include "hw/core/qdev-clock.h" #include "system/system.h" -static struct arm_boot_info aspeed_board_binfo = { - .board_id = -1, /* device-tree-only board */ -}; - #define AST_SMP_MAILBOX_BASE 0x1e6e2180 #define AST_SMP_MBOX_FIELD_ENTRY (AST_SMP_MAILBOX_BASE + 0x0) #define AST_SMP_MBOX_FIELD_GOSIGN (AST_SMP_MAILBOX_BASE + 0x4) @@ -206,13 +202,14 @@ static void aspeed_machine_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), AST_SMP_MAILBOX_BASE, smpboot); - aspeed_board_binfo.write_secondary_boot = aspeed_write_smpboot; - aspeed_board_binfo.secondary_cpu_reset_hook = aspeed_reset_secondary; - aspeed_board_binfo.smp_loader_start = AST_SMP_MBOX_CODE; + bmc->bootinfo.write_secondary_boot = aspeed_write_smpboot; + bmc->bootinfo.secondary_cpu_reset_hook = aspeed_reset_secondary; + bmc->bootinfo.smp_loader_start = AST_SMP_MBOX_CODE; } - aspeed_board_binfo.ram_size = machine->ram_size; - aspeed_board_binfo.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + bmc->bootinfo.board_id = -1; /* device-tree-only board */ + bmc->bootinfo.ram_size = machine->ram_size; + bmc->bootinfo.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; if (amc->i2c_init) { amc->i2c_init(bmc); @@ -248,7 +245,7 @@ static void aspeed_machine_init(MachineState *machine) aspeed_load_vbootrom(bmc->soc, bios_name, &error_abort); } - arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); + arm_load_kernel(ARM_CPU(first_cpu), machine, &bmc->bootinfo); } void aspeed_create_pca9552(AspeedSoCState *soc, int bus_id, int addr) diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index a00238ed74..54f0d7643d 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -12,6 +12,7 @@ #include "hw/core/boards.h" #include "qom/object.h" #include "hw/arm/aspeed_soc.h" +#include "hw/arm/boot.h" typedef struct AspeedMachineState AspeedMachineState; @@ -42,6 +43,7 @@ struct AspeedMachineState { char *fmc_model; char *spi_model; uint32_t hw_strap1; + struct arm_boot_info bootinfo; }; struct AspeedMachineClass { From 98caf5918760db50671e3351aecdaabd83317bde Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:32 +0800 Subject: [PATCH 690/860] hw/arm: aspeed_ast27x0-fc: Store boot info in the machine state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move the boot info into the existing Ast2700FCState. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260816131300.51799-3-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/aspeed_ast27x0-fc.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 058cea42ed..ceeae336df 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -27,10 +27,6 @@ #define TYPE_AST2700FC MACHINE_TYPE_NAME("ast2700fc") OBJECT_DECLARE_SIMPLE_TYPE(Ast2700FCState, AST2700FC); -static struct arm_boot_info ast2700fc_board_info = { - .board_id = -1, /* device-tree-only board */ -}; - struct Ast2700FCState { MachineState parent_obj; @@ -46,6 +42,8 @@ struct Ast2700FCState { Aspeed27x0SoCState ca35; Aspeed27x0CoprocessorState ssp; Aspeed27x0CoprocessorState tsp; + + struct arm_boot_info bootinfo; }; #define AST2700FC_BMC_RAM_SIZE (2 * GiB) @@ -114,8 +112,9 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) aspeed_board_init_flashes(&soc->fmc, AST2700FC_FMC_MODEL, 2, 0); aspeed_board_init_flashes(&soc->spi[0], AST2700FC_SPI_MODEL, 1, 2); - ast2700fc_board_info.ram_size = machine->ram_size; - ast2700fc_board_info.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + s->bootinfo.ram_size = machine->ram_size; + s->bootinfo.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + s->bootinfo.board_id = -1; /* device-tree-only board */ dev = ssi_get_cs(soc->fmc.spi, 0); fmc0 = dev ? m25p80_get_blk(dev) : NULL; @@ -129,7 +128,7 @@ static bool ast2700fc_ca35_init(MachineState *machine, Error **errp) 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); + arm_load_kernel(ARM_CPU(first_cpu), machine, &s->bootinfo); return true; } From 1fa13d2eef9ddbf0191d2c3a5a78e8b9167829a8 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:33 +0800 Subject: [PATCH 691/860] hw/arm: bananapi_m2u: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move it into a new Bpim2uMachineState and register the machine type explicitly instead of through the DEFINE_MACHINE_ARM macro. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-4-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/bananapi_m2u.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 9b468cd8ac..0079aa1dd2 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -29,7 +29,14 @@ #include "hw/arm/boot.h" #include "hw/arm/machines-qom.h" -static struct arm_boot_info bpim2u_binfo; +#define TYPE_BPIM2U_MACHINE MACHINE_TYPE_NAME("bpim2u") +OBJECT_DECLARE_SIMPLE_TYPE(Bpim2uMachineState, BPIM2U_MACHINE) + +struct Bpim2uMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; /* * R40 can boot from mmc0 and mmc2, and bpim2u has two mmc interface, one is @@ -62,6 +69,7 @@ static void mmc_attach_drive(AwR40State *s, AwSdHostState *mmc, int unit, static void bpim2u_init(MachineState *machine) { + Bpim2uMachineState *bpms = BPIM2U_MACHINE(machine); bool bootroom_loaded = false; AwR40State *r40; I2CBus *i2c; @@ -120,10 +128,10 @@ static void bpim2u_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), r40->memmap[AW_R40_DEV_SDRAM], machine->ram); - bpim2u_binfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM]; - bpim2u_binfo.ram_size = machine->ram_size; - bpim2u_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - arm_load_kernel(&r40->cpus[0], machine, &bpim2u_binfo); + bpms->bootinfo.loader_start = r40->memmap[AW_R40_DEV_SDRAM]; + bpms->bootinfo.ram_size = machine->ram_size; + bpms->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + arm_load_kernel(&r40->cpus[0], machine, &bpms->bootinfo); } static void bpim2u_machine_init(MachineClass *mc) @@ -145,4 +153,5 @@ static void bpim2u_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("bpim2u", bpim2u_machine_init) +DEFINE_MACHINE_EXTENDED("bpim2u", MACHINE, Bpim2uMachineState, + bpim2u_machine_init, false, arm_machine_interfaces) From 9130e284e4e8fdca7994bafe313dc69a03f5f5a5 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:34 +0800 Subject: [PATCH 692/860] hw/arm: collie: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move the boot info into the existing CollieMachineState. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-5-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/collie.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 91f0a94b6f..01d74ad38c 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -30,16 +30,12 @@ struct CollieMachineState { MachineState parent; StrongARMState *sa1110; + struct arm_boot_info bootinfo; }; #define TYPE_COLLIE_MACHINE MACHINE_TYPE_NAME("collie") OBJECT_DECLARE_SIMPLE_TYPE(CollieMachineState, COLLIE_MACHINE) -static struct arm_boot_info collie_binfo = { - .loader_start = SA_SDCS0, - .ram_size = RAM_SIZE, -}; - static void collie_init(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -66,8 +62,10 @@ static void collie_init(MachineState *machine) sysbus_create_simple("scoop", 0x40800000, NULL); - collie_binfo.board_id = 0x208; - arm_load_kernel(cms->sa1110->cpu, machine, &collie_binfo); + cms->bootinfo.loader_start = SA_SDCS0; + cms->bootinfo.ram_size = RAM_SIZE; + cms->bootinfo.board_id = 0x208; + arm_load_kernel(cms->sa1110->cpu, machine, &cms->bootinfo); } static void collie_machine_class_init(ObjectClass *oc, const void *data) From edb0b93a7000e6eb49cb51284555f54db6489f04 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:35 +0800 Subject: [PATCH 693/860] hw/arm: cubieboard: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move it into a new CubieboardMachineState and register the machine type explicitly instead of through the DEFINE_MACHINE_ARM macro. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-6-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/cubieboard.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index a643ae4e27..d59c1ebba7 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -25,13 +25,18 @@ #include "hw/arm/machines-qom.h" #include "hw/i2c/i2c.h" -static struct arm_boot_info cubieboard_binfo = { - .loader_start = AW_A10_SDRAM_BASE, - .board_id = 0x1008, +#define TYPE_CUBIEBOARD_MACHINE MACHINE_TYPE_NAME("cubieboard") +OBJECT_DECLARE_SIMPLE_TYPE(CubieboardMachineState, CUBIEBOARD_MACHINE) + +struct CubieboardMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; }; static void cubieboard_init(MachineState *machine) { + CubieboardMachineState *cbs = CUBIEBOARD_MACHINE(machine); AwA10State *a10; Error *err = NULL; DriveInfo *di; @@ -103,8 +108,10 @@ static void cubieboard_init(MachineState *machine) } /* TODO create and connect IDE devices for ide_drive_get() */ - cubieboard_binfo.ram_size = machine->ram_size; - arm_load_kernel(&a10->cpu, machine, &cubieboard_binfo); + cbs->bootinfo.loader_start = AW_A10_SDRAM_BASE; + cbs->bootinfo.board_id = 0x1008; + cbs->bootinfo.ram_size = machine->ram_size; + arm_load_kernel(&a10->cpu, machine, &cbs->bootinfo); } static void cubieboard_machine_init(MachineClass *mc) @@ -126,4 +133,6 @@ static void cubieboard_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("cubieboard", cubieboard_machine_init) +DEFINE_MACHINE_EXTENDED("cubieboard", MACHINE, CubieboardMachineState, + cubieboard_machine_init, false, + arm_machine_interfaces) From be525becaa224a2386499d045a6b21c59b92c3e2 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:36 +0800 Subject: [PATCH 694/860] hw/arm: exynos4_boards: Store boot info in the board state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. The Exynos4BoardState struct is already allocated per machine instance, so move the boot info there. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260816131300.51799-7-bin.meng@processmission.com Signed-off-by: Peter Maydell --- hw/arm/exynos4_boards.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 1b8c9b618f..b7decdb2c7 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -49,6 +49,7 @@ typedef struct Exynos4BoardState { Exynos4210State soc; MemoryRegion dram0_mem; MemoryRegion dram1_mem; + struct arm_boot_info bootinfo; } Exynos4BoardState; static int exynos4_board_id[EXYNOS4_NUM_OF_BOARDS] = { @@ -66,12 +67,6 @@ static unsigned long exynos4_board_ram_size[EXYNOS4_NUM_OF_BOARDS] = { [EXYNOS4_BOARD_SMDKC210] = 1 * GiB, }; -static struct arm_boot_info exynos4_board_binfo = { - .loader_start = EXYNOS4210_BASE_BOOT_ADDR, - .smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR, - .write_secondary_boot = exynos4210_write_secondary, -}; - static void lan9215_init(uint32_t base, qemu_irq irq) { DeviceState *dev; @@ -113,14 +108,15 @@ static Exynos4BoardState * exynos4_boards_init_common(MachineState *machine, Exynos4BoardType board_type) { - Exynos4BoardState *s = g_new(Exynos4BoardState, 1); + Exynos4BoardState *s = g_new0(Exynos4BoardState, 1); - exynos4_board_binfo.ram_size = exynos4_board_ram_size[board_type]; - exynos4_board_binfo.board_id = exynos4_board_id[board_type]; - exynos4_board_binfo.smp_bootreg_addr = - exynos4_board_smp_bootreg_addr[board_type]; - exynos4_board_binfo.gic_cpu_if_addr = - EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100; + s->bootinfo.loader_start = EXYNOS4210_BASE_BOOT_ADDR; + s->bootinfo.smp_loader_start = EXYNOS4210_SMP_BOOT_ADDR; + s->bootinfo.write_secondary_boot = exynos4210_write_secondary; + s->bootinfo.ram_size = exynos4_board_ram_size[board_type]; + s->bootinfo.board_id = exynos4_board_id[board_type]; + s->bootinfo.smp_bootreg_addr = exynos4_board_smp_bootreg_addr[board_type]; + s->bootinfo.gic_cpu_if_addr = EXYNOS4210_SMP_PRIVATE_BASE_ADDR + 0x100; exynos4_boards_init_ram(s, get_system_memory(), exynos4_board_ram_size[board_type]); @@ -137,7 +133,7 @@ static void nuri_init(MachineState *machine) Exynos4BoardState *s = exynos4_boards_init_common(machine, EXYNOS4_BOARD_NURI); - arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &s->bootinfo); } static void smdkc210_init(MachineState *machine) @@ -147,7 +143,7 @@ static void smdkc210_init(MachineState *machine) lan9215_init(SMDK_LAN9118_BASE_ADDR, qemu_irq_invert(s->soc.irq_table[exynos4210_get_irq(37, 1)])); - arm_load_kernel(s->soc.cpu[0], machine, &exynos4_board_binfo); + arm_load_kernel(s->soc.cpu[0], machine, &s->bootinfo); } static const char * const valid_cpu_types[] = { From bcdc392396eb3825f273e2c84c9ed7290f0ac283 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:37 +0800 Subject: [PATCH 695/860] hw/arm: imx25_pdk: Store boot info in the board state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. The IMX25PDK struct is already allocated per machine instance, so move the boot info there. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-8-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/imx25_pdk.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/arm/imx25_pdk.c b/hw/arm/imx25_pdk.c index 7ebd6c8eb9..f7a51481af 100644 --- a/hw/arm/imx25_pdk.c +++ b/hw/arm/imx25_pdk.c @@ -61,10 +61,9 @@ typedef struct IMX25PDK { FslIMX25State soc; MemoryRegion ram_alias; + struct arm_boot_info bootinfo; } IMX25PDK; -static struct arm_boot_info imx25_pdk_binfo; - static void imx25_pdk_init(MachineState *machine) { IMX25PDK *s = g_new0(IMX25PDK, 1); @@ -114,9 +113,9 @@ static void imx25_pdk_init(MachineState *machine) alias_offset += ram[i].size; } - imx25_pdk_binfo.ram_size = machine->ram_size; - imx25_pdk_binfo.loader_start = FSL_IMX25_SDRAM0_ADDR; - imx25_pdk_binfo.board_id = 1771; + s->bootinfo.ram_size = machine->ram_size; + s->bootinfo.loader_start = FSL_IMX25_SDRAM0_ADDR; + s->bootinfo.board_id = 1771; for (i = 0; i < FSL_IMX25_NUM_ESDHCS; i++) { BusState *bus; @@ -138,7 +137,7 @@ static void imx25_pdk_init(MachineState *machine) * fail. */ if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu, machine, &imx25_pdk_binfo); + arm_load_kernel(&s->soc.cpu, machine, &s->bootinfo); } } From 3b9cb3dd042c1edc4c7a240e8dcd3252e6d38dd4 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:38 +0800 Subject: [PATCH 696/860] hw/arm: imx8mm-evk: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a function scoped static object. Move it into a new Imx8mmEvkMachineState and register the machine type explicitly instead of through the DEFINE_MACHINE_AARCH64 macro. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-9-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/imx8mm-evk.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c index 6b7774d3e2..019c609c54 100644 --- a/hw/arm/imx8mm-evk.c +++ b/hw/arm/imx8mm-evk.c @@ -20,6 +20,15 @@ #include "qapi/error.h" #include +#define TYPE_IMX8MM_EVK_MACHINE MACHINE_TYPE_NAME("imx8mm-evk") +OBJECT_DECLARE_SIMPLE_TYPE(Imx8mmEvkMachineState, IMX8MM_EVK_MACHINE) + +struct Imx8mmEvkMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; + static void imx8mm_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) { int i, offset; @@ -60,7 +69,7 @@ static void imx8mm_evk_modify_dtb(const struct arm_boot_info *info, void *fdt) static void imx8mm_evk_init(MachineState *machine) { - static struct arm_boot_info boot_info; + Imx8mmEvkMachineState *ims = IMX8MM_EVK_MACHINE(machine); FslImx8mmState *s; if (machine->ram_size > FSL_IMX8MM_RAM_SIZE_MAX) { @@ -69,13 +78,11 @@ static void imx8mm_evk_init(MachineState *machine) exit(1); } - boot_info = (struct arm_boot_info) { - .loader_start = FSL_IMX8MM_RAM_START, - .board_id = -1, - .ram_size = machine->ram_size, - .psci_conduit = QEMU_PSCI_CONDUIT_SMC, - .modify_dtb = imx8mm_evk_modify_dtb, - }; + ims->bootinfo.loader_start = FSL_IMX8MM_RAM_START; + ims->bootinfo.board_id = -1; + ims->bootinfo.ram_size = machine->ram_size; + ims->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + ims->bootinfo.modify_dtb = imx8mm_evk_modify_dtb; s = FSL_IMX8MM(object_new_with_props(TYPE_FSL_IMX8MM, OBJECT(machine), "soc", &error_fatal, NULL)); @@ -103,7 +110,7 @@ static void imx8mm_evk_init(MachineState *machine) } if (!qtest_enabled()) { - arm_load_kernel(&s->cpu[0], machine, &boot_info); + arm_load_kernel(&s->cpu[0], machine, &ims->bootinfo); } } @@ -127,4 +134,6 @@ static void imx8mm_evk_machine_init(MachineClass *mc) mc->get_default_cpu_type = imx8mm_evk_get_default_cpu_type; } -DEFINE_MACHINE_AARCH64("imx8mm-evk", imx8mm_evk_machine_init) +DEFINE_MACHINE_EXTENDED("imx8mm-evk", MACHINE, Imx8mmEvkMachineState, + imx8mm_evk_machine_init, false, + aarch64_machine_interfaces) From 27d4e71757f2e08a1338541fc6e3dd55b55d4977 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:39 +0800 Subject: [PATCH 697/860] hw/arm: integratorcp: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move it into a new IntegratorcpMachineState and register the machine type explicitly instead of through the DEFINE_MACHINE_ARM macro. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-10-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/integratorcp.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index c25bbf3c82..f8bcf42c61 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -581,13 +581,19 @@ static void icp_control_init(Object *obj) /* Board init. */ -static struct arm_boot_info integrator_binfo = { - .loader_start = 0x0, - .board_id = 0x113, +#define TYPE_INTEGRATORCP_MACHINE MACHINE_TYPE_NAME("integratorcp") +OBJECT_DECLARE_SIMPLE_TYPE(IntegratorcpMachineState, + INTEGRATORCP_MACHINE) + +struct IntegratorcpMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; }; static void integratorcp_init(MachineState *machine) { + IntegratorcpMachineState *icms = INTEGRATORCP_MACHINE(machine); ram_addr_t ram_size = machine->ram_size; Object *cpuobj; ARMCPU *cpu; @@ -680,8 +686,10 @@ static void integratorcp_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xc0000000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[22]); - integrator_binfo.ram_size = ram_size; - arm_load_kernel(cpu, machine, &integrator_binfo); + icms->bootinfo.loader_start = 0x0; + icms->bootinfo.board_id = 0x113; + icms->bootinfo.ram_size = ram_size; + arm_load_kernel(cpu, machine, &icms->bootinfo); } static void integratorcp_machine_init(MachineClass *mc) @@ -696,7 +704,9 @@ static void integratorcp_machine_init(MachineClass *mc) machine_add_audiodev_property(mc); } -DEFINE_MACHINE_ARM("integratorcp", integratorcp_machine_init) +DEFINE_MACHINE_EXTENDED("integratorcp", MACHINE, IntegratorcpMachineState, + integratorcp_machine_init, false, + arm_machine_interfaces) static const Property core_properties[] = { DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), From f139ad829a21407b46beed9d165e84a98b4fa972 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:40 +0800 Subject: [PATCH 698/860] hw/arm: kzm: Store boot info in the board state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. The IMX31KZM struct is already allocated per machine instance, so move the boot info there. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-11-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/kzm.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 168ecf35d0..1aa5b49de9 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -54,17 +54,13 @@ typedef struct IMX31KZM { FslIMX31State soc; MemoryRegion ram_alias; + struct arm_boot_info bootinfo; } IMX31KZM; #define KZM_RAM_ADDR (FSL_IMX31_SDRAM0_ADDR) #define KZM_FPGA_ADDR (FSL_IMX31_CS4_ADDR + 0x1040) #define KZM_LAN9118_ADDR (FSL_IMX31_CS5_ADDR) -static struct arm_boot_info kzm_binfo = { - .loader_start = KZM_RAM_ADDR, - .board_id = 1722, -}; - static void kzm_init(MachineState *machine) { IMX31KZM *s = g_new0(IMX31KZM, 1); @@ -125,10 +121,12 @@ static void kzm_init(MachineState *machine) 14745600, serial_hd(2), DEVICE_NATIVE_ENDIAN); } - kzm_binfo.ram_size = machine->ram_size; + s->bootinfo.loader_start = KZM_RAM_ADDR; + s->bootinfo.board_id = 1722; + s->bootinfo.ram_size = machine->ram_size; if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu, machine, &kzm_binfo); + arm_load_kernel(&s->soc.cpu, machine, &s->bootinfo); } } From b8939158c37afd88ea252dc2e11ca387ffeb4ce3 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:41 +0800 Subject: [PATCH 699/860] hw/arm: mcimx7d-sabre: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a function scoped static object. Move it into a new Mcimx7dSabreMachineState and register the machine type explicitly instead of through the DEFINE_MACHINE_ARM macro. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-12-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/mcimx7d-sabre.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index a0ac647c3d..22fe1fa932 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -22,9 +22,19 @@ #include "qemu/error-report.h" #include "system/qtest.h" +#define TYPE_MCIMX7D_SABRE_MACHINE MACHINE_TYPE_NAME("mcimx7d-sabre") +OBJECT_DECLARE_SIMPLE_TYPE(Mcimx7dSabreMachineState, + MCIMX7D_SABRE_MACHINE) + +struct Mcimx7dSabreMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; + static void mcimx7d_sabre_init(MachineState *machine) { - static struct arm_boot_info boot_info; + Mcimx7dSabreMachineState *msms = MCIMX7D_SABRE_MACHINE(machine); FslIMX7State *s; int i; @@ -34,12 +44,10 @@ static void mcimx7d_sabre_init(MachineState *machine) exit(1); } - boot_info = (struct arm_boot_info) { - .loader_start = FSL_IMX7_MMDC_ADDR, - .board_id = -1, - .ram_size = machine->ram_size, - .psci_conduit = QEMU_PSCI_CONDUIT_SMC, - }; + msms->bootinfo.loader_start = FSL_IMX7_MMDC_ADDR; + msms->bootinfo.board_id = -1; + msms->bootinfo.ram_size = machine->ram_size; + msms->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; s = FSL_IMX7(object_new(TYPE_FSL_IMX7)); object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); @@ -65,7 +73,7 @@ static void mcimx7d_sabre_init(MachineState *machine) } if (!qtest_enabled()) { - arm_load_kernel(&s->cpu[0], machine, &boot_info); + arm_load_kernel(&s->cpu[0], machine, &msms->bootinfo); } } @@ -78,4 +86,6 @@ static void mcimx7d_sabre_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("mcimx7d-sabre", mcimx7d_sabre_machine_init) +DEFINE_MACHINE_EXTENDED("mcimx7d-sabre", MACHINE, Mcimx7dSabreMachineState, + mcimx7d_sabre_machine_init, false, + arm_machine_interfaces) From b8e984eb90f02df57b0b82c464170b693226d565 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:42 +0800 Subject: [PATCH 700/860] hw/arm: musicpal: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move it into a new MusicPalMachineState and register the machine type explicitly instead of through the DEFINE_MACHINE_ARM macro. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-13-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/musicpal.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 69e83bdd97..8a70ed7377 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1200,13 +1200,18 @@ static const TypeInfo musicpal_key_info = { #define FLASH_SECTOR_SIZE (64 * KiB) -static struct arm_boot_info musicpal_binfo = { - .loader_start = 0x0, - .board_id = 0x20e, +#define TYPE_MUSICPAL_MACHINE MACHINE_TYPE_NAME("musicpal") +OBJECT_DECLARE_SIMPLE_TYPE(MusicPalMachineState, MUSICPAL_MACHINE) + +struct MusicPalMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; }; static void musicpal_init(MachineState *machine) { + MusicPalMachineState *mpms = MUSICPAL_MACHINE(machine); ARMCPU *cpu; DeviceState *dev; DeviceState *pic; @@ -1341,8 +1346,10 @@ static void musicpal_init(MachineState *machine) sysbus_mmio_map(s, 0, MP_AUDIO_BASE); sysbus_connect_irq(s, 0, qdev_get_gpio_in(pic, MP_AUDIO_IRQ)); - musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; - arm_load_kernel(cpu, machine, &musicpal_binfo); + mpms->bootinfo.loader_start = 0x0; + mpms->bootinfo.board_id = 0x20e; + mpms->bootinfo.ram_size = MP_RAM_DEFAULT_SIZE; + arm_load_kernel(cpu, machine, &mpms->bootinfo); } static void musicpal_machine_init(MachineClass *mc) @@ -1357,7 +1364,9 @@ static void musicpal_machine_init(MachineClass *mc) machine_add_audiodev_property(mc); } -DEFINE_MACHINE_ARM("musicpal", musicpal_machine_init) +DEFINE_MACHINE_EXTENDED("musicpal", MACHINE, MusicPalMachineState, + musicpal_machine_init, false, + arm_machine_interfaces) static void mv88w8618_wlan_class_init(ObjectClass *klass, const void *data) { From 92fbe79cb0f5d0b72e88513205365448346d4e41 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:43 +0800 Subject: [PATCH 701/860] hw/arm: npcm7xx: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object inside npcm7xx_load_kernel(). Let the caller own the boot info: the boards store it in their NPCM7xxMachine and pass it to npcm7xx_load_kernel(), which only fills in the SoC specific values. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-14-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/npcm7xx.c | 26 ++++++++++++-------------- hw/arm/npcm7xx_boards.c | 10 +++++----- include/hw/arm/npcm7xx.h | 10 ++++++++-- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index c27f149c04..695f30a0dc 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -364,22 +364,20 @@ static void npcm7xx_write_secondary_boot(ARMCPU *cpu, NPCM7XX_SMP_LOADER_START); } -static struct arm_boot_info npcm7xx_binfo = { - .loader_start = NPCM7XX_LOADER_START, - .smp_loader_start = NPCM7XX_SMP_LOADER_START, - .smp_bootreg_addr = NPCM7XX_SMP_BOOTREG_ADDR, - .gic_cpu_if_addr = NPCM7XX_GIC_CPU_IF_ADDR, - .write_secondary_boot = npcm7xx_write_secondary_boot, - .board_id = -1, - .board_setup_addr = NPCM7XX_BOARD_SETUP_ADDR, - .write_board_setup = npcm7xx_write_board_setup, -}; - -void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc) +void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc, + struct arm_boot_info *binfo) { - npcm7xx_binfo.ram_size = machine->ram_size; + binfo->loader_start = NPCM7XX_LOADER_START; + binfo->smp_loader_start = NPCM7XX_SMP_LOADER_START; + binfo->smp_bootreg_addr = NPCM7XX_SMP_BOOTREG_ADDR; + binfo->gic_cpu_if_addr = NPCM7XX_GIC_CPU_IF_ADDR; + binfo->write_secondary_boot = npcm7xx_write_secondary_boot; + binfo->board_id = -1; + binfo->board_setup_addr = NPCM7XX_BOARD_SETUP_ADDR; + binfo->write_board_setup = npcm7xx_write_board_setup; + binfo->ram_size = machine->ram_size; - arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo); + arm_load_kernel(&soc->cpu[0], machine, binfo); } static void npcm7xx_init_fuses(NPCM7xxState *s) diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index 57a8d3186e..4f14d25746 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -374,7 +374,7 @@ static void npcm750_evb_init(MachineState *machine) npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0)); npcm750_evb_i2c_init(soc); npcm750_evb_fan_init(NPCM7XX_MACHINE(machine), soc); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void quanta_gsj_init(MachineState *machine) @@ -390,7 +390,7 @@ static void quanta_gsj_init(MachineState *machine) drive_get(IF_MTD, 0, 0)); quanta_gsj_i2c_init(soc); quanta_gsj_fan_init(NPCM7XX_MACHINE(machine), soc); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void quanta_gbs_init(MachineState *machine) @@ -408,7 +408,7 @@ static void quanta_gbs_init(MachineState *machine) quanta_gbs_i2c_init(soc); sdhci_attach_drive(&soc->mmc.sdhci, 0); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void kudo_bmc_init(MachineState *machine) @@ -427,7 +427,7 @@ static void kudo_bmc_init(MachineState *machine) kudo_bmc_i2c_init(soc); sdhci_attach_drive(&soc->mmc.sdhci, 0); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void mori_bmc_init(MachineState *machine) @@ -442,7 +442,7 @@ static void mori_bmc_init(MachineState *machine) npcm7xx_connect_flash(&soc->fiu[1], 0, "mx66u51235f", drive_get(IF_MTD, 3, 0)); - npcm7xx_load_kernel(machine, soc); + npcm7xx_load_kernel(machine, soc, &NPCM7XX_MACHINE(machine)->bootinfo); } static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type) diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index eef7cc5332..9f3dd489ec 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -19,6 +19,7 @@ #include "hw/core/boards.h" #include "hw/adc/npcm7xx_adc.h" #include "hw/core/split-irq.h" +#include "hw/arm/boot.h" #include "hw/cpu/a9mpcore.h" #include "hw/gpio/npcm7xx_gpio.h" #include "hw/i2c/npcm7xx_smbus.h" @@ -62,6 +63,7 @@ struct NPCM7xxMachine { */ SplitIRQ fan_splitter[NPCM7XX_NR_PWM_MODULES * NPCM7XX_PWM_PER_MODULE]; + struct arm_boot_info bootinfo; }; #define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx") @@ -129,11 +131,15 @@ typedef struct NPCM7xxClass { * npcm7xx_load_kernel - Loads memory with everything needed to boot * @machine - The machine containing the SoC to be booted. * @soc - The SoC containing the CPU to be booted. + * @binfo - Caller owned boot info structure to be filled in. * * This will set up the ARM boot info structure for the specific NPCM7xx * derivative and call arm_load_kernel() to set up loading of the kernel, etc. - * into memory, if requested by the user. + * into memory, if requested by the user. The boot info is owned by the + * caller because arm_load_kernel() keeps a pointer to it for the lifetime + * of the CPUs. */ -void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc); +void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc, + struct arm_boot_info *binfo); #endif /* NPCM7XX_H */ From 244b54269530468b93b4bc3676f044f3da46ea15 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:44 +0800 Subject: [PATCH 702/860] hw/arm: npcm8xx: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object inside npcm8xx_load_kernel(). Let the caller own the boot info: the board stores it in its NPCM8xxMachine and passes it to npcm8xx_load_kernel(), which only fills in the SoC specific values. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-15-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/npcm8xx.c | 26 ++++++++++++-------------- hw/arm/npcm8xx_boards.c | 2 +- include/hw/arm/npcm8xx.h | 10 ++++++++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c index 9ce6ea52d9..7d707a09b4 100644 --- a/hw/arm/npcm8xx.c +++ b/hw/arm/npcm8xx.c @@ -357,22 +357,20 @@ static const struct { }, }; -static struct arm_boot_info npcm8xx_binfo = { - .loader_start = NPCM8XX_LOADER_START, - .smp_loader_start = NPCM8XX_SMP_LOADER_START, - .smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR, - .gic_cpu_if_addr = NPCM8XX_GICC_BA, - .secure_boot = false, - .board_id = -1, - .board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR, - .psci_conduit = QEMU_PSCI_CONDUIT_SMC, -}; - -void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc) +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc, + struct arm_boot_info *binfo) { - npcm8xx_binfo.ram_size = machine->ram_size; + binfo->loader_start = NPCM8XX_LOADER_START; + binfo->smp_loader_start = NPCM8XX_SMP_LOADER_START; + binfo->smp_bootreg_addr = NPCM8XX_SMP_BOOTREG_ADDR; + binfo->gic_cpu_if_addr = NPCM8XX_GICC_BA; + binfo->secure_boot = false; + binfo->board_id = -1; + binfo->board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR; + binfo->psci_conduit = QEMU_PSCI_CONDUIT_SMC; + binfo->ram_size = machine->ram_size; - arm_load_kernel(&soc->cpu[0], machine, &npcm8xx_binfo); + arm_load_kernel(&soc->cpu[0], machine, binfo); } static void npcm8xx_init_fuses(NPCM8xxState *s) diff --git a/hw/arm/npcm8xx_boards.c b/hw/arm/npcm8xx_boards.c index 042a928857..f462369bea 100644 --- a/hw/arm/npcm8xx_boards.c +++ b/hw/arm/npcm8xx_boards.c @@ -198,7 +198,7 @@ static void npcm845_evb_init(MachineState *machine) npcm8xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0)); npcm845_evb_i2c_init(soc); npcm845_evb_fan_init(NPCM8XX_MACHINE(machine), soc); - npcm8xx_load_kernel(machine, soc); + npcm8xx_load_kernel(machine, soc, &NPCM8XX_MACHINE(machine)->bootinfo); } static void npcm8xx_set_soc_type(NPCM8xxMachineClass *nmc, const char *type) diff --git a/include/hw/arm/npcm8xx.h b/include/hw/arm/npcm8xx.h index a8377db490..676c82150d 100644 --- a/include/hw/arm/npcm8xx.h +++ b/include/hw/arm/npcm8xx.h @@ -23,6 +23,7 @@ #include "hw/i2c/npcm7xx_smbus.h" #include "hw/intc/arm_gic_common.h" #include "hw/mem/npcm7xx_mc.h" +#include "hw/arm/boot.h" #include "hw/misc/npcm_clk.h" #include "hw/misc/npcm_gcr.h" #include "hw/misc/npcm7xx_mft.h" @@ -62,6 +63,7 @@ struct NPCM8xxMachine { */ SplitIRQ fan_splitter[NPCM8XX_NR_PWM_MODULES * NPCM7XX_PWM_PER_MODULE]; + struct arm_boot_info bootinfo; }; @@ -122,11 +124,15 @@ OBJECT_DECLARE_TYPE(NPCM8xxState, NPCM8xxClass, NPCM8XX) * npcm8xx_load_kernel - Loads memory with everything needed to boot * @machine - The machine containing the SoC to be booted. * @soc - The SoC containing the CPU to be booted. + * @binfo - Caller owned boot info structure to be filled in. * * This will set up the ARM boot info structure for the specific NPCM8xx * derivative and call arm_load_kernel() to set up loading of the kernel, etc. - * into memory, if requested by the user. + * into memory, if requested by the user. The boot info is owned by the + * caller because arm_load_kernel() keeps a pointer to it for the lifetime + * of the CPUs. */ -void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc); +void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc, + struct arm_boot_info *binfo); #endif /* NPCM8XX_H */ From 53fad63eeb954200b667255b87e6d74b52720863 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:45 +0800 Subject: [PATCH 703/860] hw/arm: omap_sx1: Store boot info in the machine state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Give both the sx1 and sx1-v1 machine types the same Sx1MachineState instance struct and store the boot info there. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260816131300.51799-16-bin.meng@processmission.com Signed-off-by: Peter Maydell --- hw/arm/omap_sx1.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index bcb7105323..3b628d13cb 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -91,14 +91,16 @@ static const MemoryRegionOps static_ops = { #define FLASH1_SIZE (8 * MiB) #define FLASH2_SIZE (32 * MiB) -static struct arm_boot_info sx1_binfo = { - .loader_start = OMAP_EMIFF_BASE, - .ram_size = SDRAM_SIZE, - .board_id = 0x265, -}; +typedef struct Sx1MachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +} Sx1MachineState; static void sx1_init(MachineState *machine, const int version) { + /* Both sx1 and sx1-v1 embed the same state as their first member */ + Sx1MachineState *sms = (Sx1MachineState *)machine; struct omap_mpu_state_s *mpu; MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *address_space = get_system_memory(); @@ -187,7 +189,10 @@ static void sx1_init(MachineState *machine, const int version) } /* Load the kernel. */ - arm_load_kernel(mpu->cpu, machine, &sx1_binfo); + sms->bootinfo.loader_start = OMAP_EMIFF_BASE; + sms->bootinfo.ram_size = SDRAM_SIZE; + sms->bootinfo.board_id = 0x265; + arm_load_kernel(mpu->cpu, machine, &sms->bootinfo); /* TODO: fix next line */ //~ qemu_console_resize(ds, 640, 480); @@ -220,6 +225,7 @@ static const TypeInfo sx1_machine_v2_type = { .name = MACHINE_TYPE_NAME("sx1"), .parent = TYPE_MACHINE, .class_init = sx1_machine_v2_class_init, + .instance_size = sizeof(Sx1MachineState), .interfaces = arm_machine_interfaces, }; @@ -240,6 +246,7 @@ static const TypeInfo sx1_machine_v1_type = { .name = MACHINE_TYPE_NAME("sx1-v1"), .parent = TYPE_MACHINE, .class_init = sx1_machine_v1_class_init, + .instance_size = sizeof(Sx1MachineState), .interfaces = arm_machine_interfaces, }; From ab7794550fd24d1caca87e86f16d542195a02ce2 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:46 +0800 Subject: [PATCH 704/860] hw/arm: orangepi: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move it into a new OrangePiMachineState and register the machine type explicitly instead of through the DEFINE_MACHINE_ARM macro. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Reviewed-by: Niek Linnenbank Message-id: 20260816131300.51799-17-bin.meng@processmission.com Signed-off-by: Peter Maydell --- hw/arm/orangepi.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index d84443bb7b..0e149ed6c9 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -28,10 +28,18 @@ #include "hw/arm/boot.h" #include "hw/arm/machines-qom.h" -static struct arm_boot_info orangepi_binfo; +#define TYPE_ORANGEPI_MACHINE MACHINE_TYPE_NAME("orangepi-pc") +OBJECT_DECLARE_SIMPLE_TYPE(OrangePiMachineState, ORANGEPI_MACHINE) + +struct OrangePiMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +}; static void orangepi_init(MachineState *machine) { + OrangePiMachineState *opms = ORANGEPI_MACHINE(machine); AwH3State *h3; DriveInfo *di; BlockBackend *blk; @@ -98,10 +106,10 @@ static void orangepi_init(MachineState *machine) /* Use Boot ROM to copy data from SD card to SRAM */ allwinner_h3_bootrom_setup(h3, blk); } - orangepi_binfo.loader_start = h3->memmap[AW_H3_DEV_SDRAM]; - orangepi_binfo.ram_size = machine->ram_size; - orangepi_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - arm_load_kernel(&h3->cpus[0], machine, &orangepi_binfo); + opms->bootinfo.loader_start = h3->memmap[AW_H3_DEV_SDRAM]; + opms->bootinfo.ram_size = machine->ram_size; + opms->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; + arm_load_kernel(&h3->cpus[0], machine, &opms->bootinfo); } static void orangepi_machine_init(MachineClass *mc) @@ -125,4 +133,6 @@ static void orangepi_machine_init(MachineClass *mc) mc->auto_create_sdcard = true; } -DEFINE_MACHINE_ARM("orangepi-pc", orangepi_machine_init) +DEFINE_MACHINE_EXTENDED("orangepi-pc", MACHINE, OrangePiMachineState, + orangepi_machine_init, false, + arm_machine_interfaces) From 42a64af521af89eac0272d6705b251c1b3fbf719 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:47 +0800 Subject: [PATCH 705/860] hw/arm: realview: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Give all four realview machine types the same RealViewMachineState instance struct and store the boot info there. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-18-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/realview.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 2b9f3271d6..0c2daf5c01 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -39,10 +39,11 @@ /* Board init. */ -static struct arm_boot_info realview_binfo = { - .smp_loader_start = SMP_BOOT_ADDR, - .smp_bootreg_addr = SMP_BOOTREG_ADDR, -}; +typedef struct RealViewMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +} RealViewMachineState; /* The following two lists must be consistent. */ enum realview_board_type { @@ -76,6 +77,8 @@ static void split_irq_from_named(DeviceState *src, const char* outname, static void realview_init(MachineState *machine, enum realview_board_type board_type) { + /* All realview-* machines embed the same state as their first member */ + RealViewMachineState *rvms = (RealViewMachineState *)machine; ARMCPU *cpu = NULL; CPUARMState *env; MemoryRegion *sysmem = get_system_memory(); @@ -202,7 +205,7 @@ static void realview_init(MachineState *machine, } sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL); /* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */ - realview_binfo.gic_cpu_if_addr = periphbase + 0x100; + rvms->bootinfo.gic_cpu_if_addr = periphbase + 0x100; } else { uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000; /* For now just create the nIRQ GIC, and ignore the others. */ @@ -387,10 +390,12 @@ static void realview_init(MachineState *machine, &error_fatal); memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, ram_hack); - realview_binfo.ram_size = ram_size; - realview_binfo.board_id = realview_board_id[board_type]; - realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0); - arm_load_kernel(cpu, machine, &realview_binfo); + rvms->bootinfo.smp_loader_start = SMP_BOOT_ADDR; + rvms->bootinfo.smp_bootreg_addr = SMP_BOOTREG_ADDR; + rvms->bootinfo.ram_size = ram_size; + rvms->bootinfo.board_id = realview_board_id[board_type]; + rvms->bootinfo.loader_start = board_type == BOARD_PB_A8 ? 0x70000000 : 0; + arm_load_kernel(cpu, machine, &rvms->bootinfo); } static void realview_eb_init(MachineState *machine) @@ -431,6 +436,7 @@ static const TypeInfo realview_eb_type = { .name = MACHINE_TYPE_NAME("realview-eb"), .parent = TYPE_MACHINE, .class_init = realview_eb_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; @@ -453,6 +459,7 @@ static const TypeInfo realview_eb_mpcore_type = { .name = MACHINE_TYPE_NAME("realview-eb-mpcore"), .parent = TYPE_MACHINE, .class_init = realview_eb_mpcore_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; @@ -473,6 +480,7 @@ static const TypeInfo realview_pb_a8_type = { .name = MACHINE_TYPE_NAME("realview-pb-a8"), .parent = TYPE_MACHINE, .class_init = realview_pb_a8_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; @@ -494,6 +502,7 @@ static const TypeInfo realview_pbx_a9_type = { .name = MACHINE_TYPE_NAME("realview-pbx-a9"), .parent = TYPE_MACHINE, .class_init = realview_pbx_a9_class_init, + .instance_size = sizeof(RealViewMachineState), .interfaces = arm_machine_interfaces, }; From 4a421f0a7b9a6ee8221480fb026a2da9ebad3572 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:48 +0800 Subject: [PATCH 706/860] hw/arm: sabrelite: Store boot info in the machine state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move the boot info into the existing SabreliteMachineState. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Message-id: 20260816131300.51799-19-bin.meng@processmission.com Signed-off-by: Peter Maydell --- hw/arm/sabrelite.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hw/arm/sabrelite.c b/hw/arm/sabrelite.c index 84bbc2f979..140b2ecb9e 100644 --- a/hw/arm/sabrelite.c +++ b/hw/arm/sabrelite.c @@ -25,18 +25,12 @@ struct SabreliteMachineState { FslIMX6State soc; CanBusState *canbus[FSL_IMX6_NUM_CANS]; + struct arm_boot_info bootinfo; }; #define TYPE_SABRELITE_MACHINE MACHINE_TYPE_NAME("sabrelite") OBJECT_DECLARE_SIMPLE_TYPE(SabreliteMachineState, SABRELITE_MACHINE) -static struct arm_boot_info sabrelite_binfo = { - /* DDR memory start */ - .loader_start = FSL_IMX6_MMDC_ADDR, - /* No board ID, we boot from DT tree */ - .board_id = -1, -}; - /* No need to do any particular setup for secondary boot */ static void sabrelite_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info) @@ -110,13 +104,17 @@ static void sabrelite_init(MachineState *machine) } } - sabrelite_binfo.ram_size = machine->ram_size; - sabrelite_binfo.secure_boot = true; - sabrelite_binfo.write_secondary_boot = sabrelite_write_secondary; - sabrelite_binfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; + /* DDR memory start */ + s->bootinfo.loader_start = FSL_IMX6_MMDC_ADDR; + /* No board ID, we boot from DT tree */ + s->bootinfo.board_id = -1; + s->bootinfo.ram_size = machine->ram_size; + s->bootinfo.secure_boot = true; + s->bootinfo.write_secondary_boot = sabrelite_write_secondary; + s->bootinfo.secondary_cpu_reset_hook = sabrelite_reset_secondary; if (!qtest_enabled()) { - arm_load_kernel(&s->soc.cpu[0], machine, &sabrelite_binfo); + arm_load_kernel(&s->soc.cpu[0], machine, &s->bootinfo); } } From 4477c1448252fc33c978db30e96d1787ebd4df80 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:49 +0800 Subject: [PATCH 707/860] hw/arm: versatilepb: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Give both machine types the same VersatileMachineState instance struct and store the boot info there. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Message-id: 20260816131300.51799-20-bin.meng@processmission.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/versatilepb.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index c6991a52e6..520af79c80 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -182,10 +182,16 @@ static void vpb_sic_init(Object *obj) peripherals and expansion busses. For now we emulate a subset of the PB peripherals and just change the board ID. */ -static struct arm_boot_info versatile_binfo; +typedef struct VersatileMachineState { + MachineState parent; + + struct arm_boot_info bootinfo; +} VersatileMachineState; static void versatile_init(MachineState *machine, int board_id) { + /* versatilepb and versatileab embed the same state as first member */ + VersatileMachineState *vms = (VersatileMachineState *)machine; Object *cpuobj; ARMCPU *cpu; MemoryRegion *sysmem = get_system_memory(); @@ -397,9 +403,9 @@ static void versatile_init(MachineState *machine, int board_id) VERSATILE_FLASH_SECT_SIZE, 4, 0x0089, 0x0018, 0x0000, 0x0, 0); - versatile_binfo.ram_size = machine->ram_size; - versatile_binfo.board_id = board_id; - arm_load_kernel(cpu, machine, &versatile_binfo); + vms->bootinfo.ram_size = machine->ram_size; + vms->bootinfo.board_id = board_id; + arm_load_kernel(cpu, machine, &vms->bootinfo); } static void vpb_init(MachineState *machine) @@ -431,6 +437,7 @@ static const TypeInfo versatilepb_type = { .name = MACHINE_TYPE_NAME("versatilepb"), .parent = TYPE_MACHINE, .class_init = versatilepb_class_init, + .instance_size = sizeof(VersatileMachineState), .interfaces = arm_machine_interfaces, }; @@ -453,6 +460,7 @@ static const TypeInfo versatileab_type = { .name = MACHINE_TYPE_NAME("versatileab"), .parent = TYPE_MACHINE, .class_init = versatileab_class_init, + .instance_size = sizeof(VersatileMachineState), .interfaces = arm_machine_interfaces, }; From 37c0a0059db0dd6f35945196ddb17684c458886a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Sun, 16 Aug 2026 21:12:50 +0800 Subject: [PATCH 708/860] hw/arm: xilinx_zynq: Store boot info in the machine state arm_load_kernel() keeps a pointer to the boot info struct for the lifetime of the VM, so the struct logically belongs to the machine rather than to a file scoped static object. Move the boot info into the existing ZynqMachineState. As in the xlnx-zcu102 and raspi machines, the boot info belongs to the machine rather than to a static object: 4d1ac883a7 ("hw/arm: xlnx-zcu102: Move arm_boot_info into XlnxZCU102") 0f15c6e338 ("hw/arm/raspi: Move arm_boot_info structure to RaspiMachineState") Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-id: 20260816131300.51799-21-bin.meng@processmission.com Signed-off-by: Peter Maydell --- hw/arm/xilinx_zynq.c | 17 ++++++++--------- include/hw/arm/xilinx_zynq.h | 2 ++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 6c83439017..f314897540 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -103,8 +103,6 @@ static void zynq_write_board_setup(ARMCPU *cpu, sizeof(board_setup_blob), BOARD_SETUP_ADDR); } -static struct arm_boot_info zynq_binfo = {}; - static void gem_init(uint32_t base, qemu_irq irq) { DeviceState *dev; @@ -268,7 +266,7 @@ static void zynq_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); sysbus_mmio_map(busdev, 0, MPCORE_PERIPHBASE); - zynq_binfo.gic_cpu_if_addr = MPCORE_PERIPHBASE + 0x100; + zynq_machine->bootinfo.gic_cpu_if_addr = MPCORE_PERIPHBASE + 0x100; sysbus_create_varargs("l2x0", MPCORE_PERIPHBASE + 0x2000, NULL); for (n = 0; n < smp_cpus; n++) { /* See "hw/intc/arm_gic.h" for the IRQ line association */ @@ -444,13 +442,14 @@ static void zynq_init(MachineState *machine) create_unimplemented_device("zynq.qos301_dmac", 0xF8947000, 0x130); create_unimplemented_device("zynq.qos301_iou", 0xF8948000, 0x130); - zynq_binfo.ram_size = machine->ram_size; - zynq_binfo.board_id = 0xd32; - zynq_binfo.loader_start = 0; - zynq_binfo.board_setup_addr = BOARD_SETUP_ADDR; - zynq_binfo.write_board_setup = zynq_write_board_setup; + zynq_machine->bootinfo.ram_size = machine->ram_size; + zynq_machine->bootinfo.board_id = 0xd32; + zynq_machine->bootinfo.loader_start = 0; + zynq_machine->bootinfo.board_setup_addr = BOARD_SETUP_ADDR; + zynq_machine->bootinfo.write_board_setup = zynq_write_board_setup; - arm_load_kernel(zynq_machine->cpu[0], machine, &zynq_binfo); + arm_load_kernel(zynq_machine->cpu[0], machine, + &zynq_machine->bootinfo); } static void zynq_machine_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/xilinx_zynq.h b/include/hw/arm/xilinx_zynq.h index cefb7789ff..669c516ad1 100644 --- a/include/hw/arm/xilinx_zynq.h +++ b/include/hw/arm/xilinx_zynq.h @@ -14,6 +14,7 @@ #include "target/arm/cpu-qom.h" #include "hw/core/qdev-clock.h" +#include "hw/arm/boot.h" #define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9") OBJECT_DECLARE_SIMPLE_TYPE(ZynqMachineState, ZYNQ_MACHINE) @@ -25,6 +26,7 @@ struct ZynqMachineState { Clock *ps_clk; ARMCPU *cpu[ZYNQ_MAX_CPUS]; uint8_t boot_mode; + struct arm_boot_info bootinfo; }; #endif /* QEMU_ARM_ZYNQ_H */ From fa6a91fd8cb917fbe08b701f861d8ece9d571b4e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 20 Jul 2026 19:24:03 +0000 Subject: [PATCH 709/860] target/ppc/cpu_init: make cpu listing deterministic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing compare function only make sure that we list cpus with PVR order. However, we never compare cpu names. As a result, while cpus are grouped per PVR, the order within a group is non deterministic. Depending on QOM type initialization order, we get different results for -cpu help. For instance, previous output could be: Available CPUs: 755_v1.0 PVR 00083100 745_v1.0 PVR 00083100 755_v1.1 PVR 00083101 745_v1.1 PVR 00083101 While a sorted output should be: Available CPUs: 745_v1.0 PVR 00083100 755_v1.0 PVR 00083100 745_v1.1 PVR 00083101 755_v1.1 PVR 00083101 Fix it by comparing cpu names to have a stable result. This allows us to record and compare various command line results across versions to make sure we didn't break anything while working on single-binary. Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Amit Machhiwal Tested-by: Amit Machhiwal Link: https://lore.kernel.org/qemu-devel/20260720192403.66694-1-pierrick.bouvier@oss.qualcomm.com Signed-off-by: Harsh Prateek Bora --- target/ppc/cpu_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index e3a1075aad..e2533481d8 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7130,7 +7130,7 @@ static gint ppc_cpu_list_compare(gconstpointer a, gconstpointer b, gpointer d) } else if (pcc_a->pvr > pcc_b->pvr) { return 1; } else { - return 0; + return strcmp(name_a, name_b); } } } From e2bbc72a66674315a8e13c77f5ecaffd7edc398a Mon Sep 17 00:00:00 2001 From: Minhang Zhang Date: Fri, 14 Aug 2026 17:26:32 +0800 Subject: [PATCH 710/860] target/ppc: Validate HTABMASK and reserved bits in SDR1 for 32-bit mode ppc_store_sdr1() had validation for 64-bit SDR1 values but lacked corresponding checks for the 32-bit case. According to the Power ISA, in 32-bit mode SDR1 bits 16-22 are reserved (must be zero) and HTABMASK (bits 23-31) must consist of a consecutive string of 1-bits starting from the LSB, i.e., be of the form 2^n-1. Add checks to reject invalid HTABMASK values and log a guest error for non-zero reserved bits, following the same pattern used by the existing 64-bit validation. Reviewed-by: Chinmay Rath Signed-off-by: Minhang Zhang Link: https://lore.kernel.org/qemu-devel/tencent_8388D4B38DC5172F5EEE70436D3AD6D02006@qq.com Signed-off-by: Harsh Prateek Bora --- target/ppc/mmu_common.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 2499e619f8..2a36817181 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -42,24 +42,39 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) PowerPCCPU *cpu = env_archcpu(env); qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); assert(!cpu->env.has_hv_mode || !cpu->vhyp); -#if defined(TARGET_PPC64) if (mmu_is_64bit(env->mmu_model)) { +#if defined(TARGET_PPC64) target_ulong sdr_mask = SDR_64_HTABORG | SDR_64_HTABSIZE; target_ulong htabsize = value & SDR_64_HTABSIZE; if (value & ~sdr_mask) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid bits 0x"TARGET_FMT_lx - " set in SDR1", value & ~sdr_mask); + " set in SDR1\n", value & ~sdr_mask); value &= sdr_mask; } if (htabsize > 28) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid HTABSIZE 0x" TARGET_FMT_lx - " stored in SDR1", htabsize); + " stored in SDR1\n", htabsize); + return; + } +#endif /* defined(TARGET_PPC64) */ + } else { + target_ulong sdr_mask = SDR_32_HTABORG | SDR_32_HTABMASK; + target_ulong htabmask = value & SDR_32_HTABMASK; + + if (value & ~sdr_mask) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid bits 0x" TARGET_FMT_lx + " set in SDR1\n", value & ~sdr_mask); + value &= sdr_mask; + } + if ((htabmask & (htabmask + 1)) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid HTABMASK 0x" TARGET_FMT_lx + " in SDR1 (must be of form 2^n-1)\n", htabmask); return; } } -#endif /* defined(TARGET_PPC64) */ - /* FIXME: Should check for valid HTABMASK values in 32-bit case */ env->spr[SPR_SDR1] = value; } From ac6a32a106666e8b75e83d1a9e14fa653c1e3dae Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Wed, 19 Aug 2026 10:35:45 +0530 Subject: [PATCH 711/860] MAINTAINERS: Add myself as a reviewer for PPC KVM and sPAPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have been contributing to PPC KVM and sPAPR (pseries) for some time now and would like to get notified of incoming changes to help with code reviews. Signed-off-by: Amit Machhiwal Acked-by: Gautam Menghani Acked-by: Chinmay Rath Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20260819050545.21232-1-amachhiw@linux.ibm.com Signed-off-by: Harsh Prateek Bora --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 08017f4786..79e6c8e0b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -497,6 +497,7 @@ F: tests/functional/aarch64/test_kvm.py PPC KVM CPUs M: Nicholas Piggin R: Harsh Prateek Bora +R: Amit Machhiwal S: Odd Fixes F: target/ppc/kvm.c @@ -1662,6 +1663,7 @@ F: tests/functional/ppc/test_40p.py sPAPR (pseries) M: Nicholas Piggin M: Harsh Prateek Bora +R: Amit Machhiwal L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/*/spapr* From eea4de1bd8e8bdaa9a69b130a1a154f7c113979f Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Tue, 18 Aug 2026 16:15:51 +0530 Subject: [PATCH 712/860] hw/watchdog: Add lower bound check for watchdogNumber Add missing lower bound check for H_WATCHDOG H_CALL's watchdogNumber parameter as per PAPR documentation ver 12.10.00 section 14.15.5 'H_WATCHDOG'. Closes : https://gitlab.com/qemu-project/qemu/-/work_items/3600 Reviewed-by: Amit Machhiwal Reported-by: huntr bubble Signed-off-by: Chinmay Rath Link: https://lore.kernel.org/qemu-devel/20260818104551.76023-1-rathc@linux.ibm.com [harshpb: corrected title prefix to hw/watchdog] Signed-off-by: Harsh Prateek Bora --- hw/watchdog/spapr_watchdog.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c index 5b3f50de3a..5a72896066 100644 --- a/hw/watchdog/spapr_watchdog.c +++ b/hw/watchdog/spapr_watchdog.c @@ -127,6 +127,12 @@ static void watchdog_expired(void *pw) } } +static inline bool watchdog_number_valid(target_ulong watchdogNumber, + SpaprMachineState *spapr) +{ + return watchdogNumber >= 1 && watchdogNumber <= ARRAY_SIZE(spapr->wds); +} + static target_ulong h_watchdog(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) @@ -145,7 +151,7 @@ static target_ulong h_watchdog(PowerPCCPU *cpu, switch (operation) { case PSERIES_WDTF_OP_START: - if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + if (!watchdog_number_valid(watchdogNumber, spapr)) { return H_P2; } if (timeoutInMs <= WDT_MIN_TIMEOUT) { @@ -170,11 +176,11 @@ static target_ulong h_watchdog(PowerPCCPU *cpu, case PSERIES_WDTF_OP_STOP: if (watchdogNumber == PSERIES_WDT_STOP_ALL) { ret = watchdog_stop_all(spapr); - } else if (watchdogNumber <= ARRAY_SIZE(spapr->wds)) { + } else if (!watchdog_number_valid(watchdogNumber, spapr)) { + return H_P2; + } else { ret = watchdog_stop(watchdogNumber, &spapr->wds[watchdogNumber - 1]); - } else { - return H_P2; } break; case PSERIES_WDTF_OP_QUERY: @@ -184,7 +190,7 @@ static target_ulong h_watchdog(PowerPCCPU *cpu, trace_spapr_watchdog_query(args[0]); break; case PSERIES_WDTF_OP_QUERY_LPM: - if (watchdogNumber > ARRAY_SIZE(spapr->wds)) { + if (!watchdog_number_valid(watchdogNumber, spapr)) { return H_P2; } args[0] = PSERIES_WDTQL_QUERY_NOT_STOPPED; From 504810bbdfd9c65a808b4684b2b0f6871f2d8c1c Mon Sep 17 00:00:00 2001 From: "Kirill A. Korinsky" Date: Sun, 19 Jul 2026 16:55:07 +0200 Subject: [PATCH 713/860] powernv: boot OpenBSD on POWER9 The POWER9 Processor User's Manual, section 4.9.4, specifies that POWER9 ignores PTCR[PATS] and only supports a 64 KiB partition table. Use an effective PATS value of 4 on POWER9; other processors keep the existing ISA v3.0 interpretation. The PSI model now exposes POWER9 IRQ level and pending status registers, and keeps both updated while delivering through the existing XIVE LSI source. This lets guests that select the POWER9 PSI LSI IRQ method continue to receive LPC interrupts. The blast radius is probably minimal: the PTCR change is limited to POWER9, while the PSI change only touches POWER9 PSI state and reuses the existing delivery path. Acked-by: Chinmay Rath Signed-off-by: Kirill A. Korinsky Reviewed-by: Aditya Gupta Link: https://lore.kernel.org/qemu-devel/20260719145559.94342-1-kirill@korins.ky Signed-off-by: Harsh Prateek Bora --- hw/ppc/pnv_psi.c | 11 ++++-- target/ppc/mmu-book3s-v3.c | 21 +++++++++-- tests/functional/ppc64/meson.build | 2 + tests/functional/ppc64/test_openbsd.py | 52 ++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 7 deletions(-) create mode 100755 tests/functional/ppc64/test_openbsd.py diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index e8701c6100..39ec448f3c 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -688,6 +688,8 @@ static uint64_t pnv_psi_p9_mmio_read(void *opaque, hwaddr addr, unsigned size) case PSIHB9_ESB_CI_BASE: case PSIHB9_ESB_NOTIF_ADDR: case PSIHB9_IVT_OFFSET: + case PSIHB9_IRQ_LEVEL: + case PSIHB9_IRQ_STAT: val = psi->regs[reg]; break; default: @@ -818,17 +820,20 @@ static void pnv_psi_power9_set_irq(void *opaque, int irq, int state) { PnvPsi *psi = opaque; uint64_t irq_method = psi->regs[PSIHB_REG(PSIHB9_INTERRUPT_CONTROL)]; + uint64_t irq_bit = PPC_BIT(irq); if (irq_method & PSIHB9_IRQ_METHOD) { qemu_log_mask(LOG_GUEST_ERROR, "PSI: LSI IRQ method no supported\n"); return; } - /* Update LSI levels */ + /* Update LSI levels and pending status */ if (state) { - psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] |= PPC_BIT(irq); + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] |= irq_bit; + psi->regs[PSIHB_REG(PSIHB9_IRQ_STAT)] |= irq_bit; } else { - psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] &= ~PPC_BIT(irq); + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] &= ~irq_bit; + psi->regs[PSIHB_REG(PSIHB9_IRQ_STAT)] &= ~irq_bit; } qemu_set_irq(psi->qirqs[irq], state); diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index 3865556310..f329a7a0f2 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -23,24 +23,37 @@ #include "mmu-hash64.h" #include "mmu-book3s-v3.h" +#define PPC64_V3_PATE_SIZE 16 /* two 64-bit words */ + bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) { uint64_t patb = cpu->env.spr[SPR_PTCR] & PTCR_PATB; uint64_t pats = cpu->env.spr[SPR_PTCR] & PTCR_PATS; + uint64_t table_size; + uint64_t entries; + + /* + * The POWER9 Processor User's Manual, section 4.9.4, specifies that + * POWER9 ignores PTCR[PATS] and only supports a 64 KiB partition table. + */ + if (cpu->env.excp_model == POWERPC_EXCP_POWER9) { + pats = 4; + } + table_size = 1ULL << (pats + 12); /* Check if partition table is properly aligned */ - if (patb & MAKE_64BIT_MASK(0, pats + 12)) { + if (patb & (table_size - 1)) { return false; } /* Calculate number of entries */ - pats = 1ull << (pats + 12 - 4); - if (pats <= lpid) { + entries = table_size / PPC64_V3_PATE_SIZE; + if (entries <= lpid) { return false; } /* Grab entry */ - patb += 16 * lpid; + patb += PPC64_V3_PATE_SIZE * lpid; entry->dw0 = ldq_phys(CPU(cpu)->as, patb); entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8); return true; diff --git a/tests/functional/ppc64/meson.build b/tests/functional/ppc64/meson.build index f0f8ab8f61..cb3c745624 100644 --- a/tests/functional/ppc64/meson.build +++ b/tests/functional/ppc64/meson.build @@ -4,6 +4,7 @@ test_ppc64_timeouts = { 'fadump' : 480, 'hv' : 1000, 'mac99' : 120, + 'openbsd' : 240, 'powernv' : 480, 'pseries' : 480, 'replay' : 210, @@ -20,6 +21,7 @@ tests_ppc64_system_thorough = [ 'fadump', 'hv', 'mac99', + 'openbsd', 'powernv', 'pseries', 'replay', diff --git a/tests/functional/ppc64/test_openbsd.py b/tests/functional/ppc64/test_openbsd.py new file mode 100755 index 0000000000..bdbef6bf82 --- /dev/null +++ b/tests/functional/ppc64/test_openbsd.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Test that OpenBSD boots on a ppc powernv machine and reaches the installer. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + + +class OpenBSDPowerNV(QemuSystemTest): + + ASSET_MINIROOT = Asset( + 'https://kirill.korins.ky/pub/qemu-powerpc64-openbsd/miniroot79.img', + '7829e42b75d81cafd732038b9d63228b79c1f5828d8375872a4bb655e1d6b13c') + + ASSET_BOOTKERNEL = Asset( + 'https://kirill.korins.ky/pub/qemu-powerpc64-openbsd/pnor.BOOTKERNEL', + '397ce43ce61910e1a2c4f13d301f957e61513a9ec5371bc3e87d3095411fae7b') + + def test_powernv9_openbsd_installer(self): + self.set_machine('powernv9') + self.require_accelerator('tcg') + + miniroot_path = self.ASSET_MINIROOT.fetch() + bootkernel_path = self.ASSET_BOOTKERNEL.fetch() + + self.vm.set_console() + self.vm.add_args('-cpu', 'power9', + '-accel', 'tcg,thread=single', + '-smp', '1,cores=1,threads=1', + '-m', '2g', + '-kernel', bootkernel_path, + '-device', + 'ich9-ahci,id=sata0,bus=pcie.0,addr=0x0', + '-drive', + f'file={miniroot_path},format=raw,if=none,' + 'id=bootdisk,snapshot=on', + '-device', + 'ide-hd,bus=sata0.0,unit=0,drive=bootdisk,' + 'bootindex=1') + self.vm.launch() + + wait_for_console_pattern(self, 'OpenBSD 7.9 (RAMDISK)', 'panic:') + wait_for_console_pattern( + self, + '(I)nstall, (U)pgrade, (A)utoinstall or (S)hell?', + 'panic:') + + +if __name__ == '__main__': + QemuSystemTest.main() From 3e182317a65a217fc319fa88f2697d220781270b Mon Sep 17 00:00:00 2001 From: Shivang Upadhyay Date: Thu, 20 Aug 2026 01:07:54 +0530 Subject: [PATCH 714/860] MAINTAINERS: add dedicated PowerPC RAS section Introduce a new "PowerPC RAS (Reliability, Availability and Serviceability)" entry in the PowerPC Machines block, replacing the existing Fadump/MPIPL sections. Retaining the maintainer and reviewer entries from Fadump/MPIPL sections for this broader umbrella. Currently most of RAS related code sits in spapr_rtas.c, So Adding it to PowerPC RAS section. Additionally adding spapr_events.c, as it implements RTAS error logging infrastructure and spapr_pci_vfio.c, since its all EEH related code. Acked-by: Sourabh Jain Acked-by: Aditya Gupta Signed-off-by: Shivang Upadhyay Acked-by: Hari Bathini Link: https://lore.kernel.org/qemu-devel/20260819193757.1072909-2-shivangu@linux.ibm.com Signed-off-by: Harsh Prateek Bora --- MAINTAINERS | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 79e6c8e0b2..c47e0790ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1765,6 +1765,21 @@ F: include/hw/ppc/vof* F: pc-bios/vof/* F: pc-bios/vof* +PowerPC RAS (Reliability, Availability and Serviceability) +M: Aditya Gupta +R: Sourabh Jain +R: Hari Bathini +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/spapr_events.c +F: hw/ppc/spapr_fadump.c +F: hw/ppc/spapr_pci_vfio.c +F: hw/ppc/spapr_rtas.c +F: hw/ppc/pnv_mpipl.c +F: include/hw/ppc/spapr_fadump.h +F: include/hw/ppc/pnv_mpipl.h +F: tests/functional/ppc64/test_fadump.py + RISC-V Machines --------------- OpenTitan @@ -3402,23 +3417,6 @@ F: scripts/coccinelle/remove_local_err.cocci F: scripts/coccinelle/use-error_fatal.cocci F: scripts/coccinelle/errp-guard.cocci -Firmware Assisted Dump (fadump) for sPAPR (pseries) -M: Aditya Gupta -R: Sourabh Jain -S: Maintained -F: include/hw/ppc/spapr_fadump.h -F: hw/ppc/spapr_fadump.c -F: tests/functional/ppc64/test_fadump.py - -Memory-Preserving Initial Program Load (MPIPL) for PowerNV -M: Aditya Gupta -R: Hari Bathini -R: Sourabh -S: Maintained -F: include/hw/ppc/pnv_mpipl.h -F: hw/ppc/pnv_mpipl.c -F: tests/functional/ppc64/test_fadump.py - GDB stub M: Alex Bennée R: Philippe Mathieu-Daudé From 45caf67b9a6a446629e1c133b76a3d9a3cebdcf1 Mon Sep 17 00:00:00 2001 From: Shivang Upadhyay Date: Thu, 20 Aug 2026 01:07:55 +0530 Subject: [PATCH 715/860] MAINTAINERS: add self as reviewer for PowerPC RAS I have been contributing to Fadump, MPIPL as well as PowerNV for quite some time, and my daily work responsibilities includes taking care of PowerPC RAS features. I, therefore would like to step up as a reviewer to get notified of incoming changes in this area and help reviewing them. Acked-by: Aditya Gupta Signed-off-by: Shivang Upadhyay Link: https://lore.kernel.org/qemu-devel/20260819193757.1072909-3-shivangu@linux.ibm.com Signed-off-by: Harsh Prateek Bora --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index c47e0790ba..60c7f51c23 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1769,6 +1769,7 @@ PowerPC RAS (Reliability, Availability and Serviceability) M: Aditya Gupta R: Sourabh Jain R: Hari Bathini +R: Shivang Upadhyay L: qemu-ppc@nongnu.org S: Maintained F: hw/ppc/spapr_events.c From 798eddba103250385a3b2973890e0b757704b584 Mon Sep 17 00:00:00 2001 From: Junze Cao Date: Fri, 7 Aug 2026 18:21:16 +0800 Subject: [PATCH 716/860] hw/misc: Add K230 DDR controller and PHY models The K230 SDK U-Boot SPL programs K230 DDRC CFG and K230 DDR PHY registers before DRAM can be used. Add separate SysBus devices for both register ranges. Model controller reset values, DFI and software-update handshakes. For the PHY, model register ownership, the training mailbox, and DFI completion. Include migration state for both devices. Signed-off-by: Junze Cao Suggested-by: Chao Liu Reviewed-by: Daniel Henrique Barboza Message-ID: <20260807-k230-ddr-v2-v2-1-c531308ae819@gmail.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 2 + hw/misc/k230_ddr.c | 556 +++++++++++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + include/hw/misc/k230_ddr.h | 59 ++++ 4 files changed, 618 insertions(+) create mode 100644 hw/misc/k230_ddr.c create mode 100644 include/hw/misc/k230_ddr.h diff --git a/MAINTAINERS b/MAINTAINERS index 08017f4786..cc0879c868 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1844,8 +1844,10 @@ M: Chao Liu L: qemu-riscv@nongnu.org S: Maintained F: docs/system/riscv/k230.rst +F: hw/misc/k230_ddr.c F: hw/riscv/k230.c F: hw/watchdog/k230_wdt.c +F: include/hw/misc/k230_ddr.h F: include/hw/riscv/k230.h F: include/hw/watchdog/k230_wdt.h F: tests/functional/riscv64/test_k230.py diff --git a/hw/misc/k230_ddr.c b/hw/misc/k230_ddr.c new file mode 100644 index 0000000000..dac700b201 --- /dev/null +++ b/hw/misc/k230_ddr.c @@ -0,0 +1,556 @@ +/* + * Kendryte K230 DDR controller and PHY models + * + * Models the K230 DDRC CFG registers and K230 DDR PHY registers exercised + * by the K230 SDK U-Boot SPL. + * + * Copyright (c) 2026 Junze Cao + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "migration/vmstate.h" +#include "hw/core/registerfields.h" +#include "hw/core/resettable.h" +#include "hw/misc/k230_ddr.h" + +/* DDRC CFG registers */ + +enum K230DDRCOperatingMode { + K230_DDRC_MODE_INIT = 0, + K230_DDRC_MODE_NORMAL = 1, +}; + +REG32(K230_DDRC_MSTR, 0x000) + +REG32(K230_DDRC_STAT, 0x004) + FIELD(K230_DDRC_STAT, OPERATING_MODE, 0, 3) + +REG32(K230_DDRC_PWRCTL, 0x030) + +REG32(K230_DDRC_RFSHCTL0, 0x050) + +REG32(K230_DDRC_RFSHTMG, 0x064) + +REG32(K230_DDRC_RFSHTMG1, 0x068) + +REG32(K230_DDRC_INIT0, 0x0d0) + +REG32(K230_DDRC_INIT2, 0x0d8) + +REG32(K230_DDRC_INIT3, 0x0dc) + +REG32(K230_DDRC_INIT5, 0x0e4) + +REG32(K230_DDRC_DRAMTMG0, 0x100) +#define R_K230_DDRC_DRAMTMG(index) \ + (R_K230_DDRC_DRAMTMG0 + (index)) + +REG32(K230_DDRC_ZQCTL0, 0x180) + +REG32(K230_DDRC_ZQCTL1, 0x184) + +REG32(K230_DDRC_ZQCTL2, 0x188) + +REG32(K230_DDRC_ZQSTAT, 0x18c) + +REG32(K230_DDRC_DFITMG0, 0x190) + +REG32(K230_DDRC_DFITMG1, 0x194) + +REG32(K230_DDRC_DFIUPD0, 0x1a0) + +REG32(K230_DDRC_DFIUPD1, 0x1a4) + +REG32(K230_DDRC_DFIUPD2, 0x1a8) + +REG32(K230_DDRC_DFIMISC, 0x1b0) + FIELD(K230_DDRC_DFIMISC, DFI_INIT_COMPLETE_EN, 0, 1) + FIELD(K230_DDRC_DFIMISC, DFI_INIT_START, 5, 1) + +REG32(K230_DDRC_DFITMG2, 0x1b4) + +REG32(K230_DDRC_DFISTAT, 0x1bc) + FIELD(K230_DDRC_DFISTAT, DFI_INIT_COMPLETE, 0, 1) + +REG32(K230_DDRC_ODTCFG, 0x240) + +REG32(K230_DDRC_SWCTL, 0x320) + FIELD(K230_DDRC_SWCTL, SW_DONE, 0, 1) + +REG32(K230_DDRC_SWSTAT, 0x324) + FIELD(K230_DDRC_SWSTAT, SW_DONE_ACK, 0, 1) + +static const uint32_t k230_ddrc_reset_values[K230_DDRC_REG_COUNT] = { + [R_K230_DDRC_MSTR] = 0x01040000, + [R_K230_DDRC_RFSHCTL0] = 0x00210000, + [R_K230_DDRC_RFSHTMG] = 0x0062008c, + [R_K230_DDRC_RFSHTMG1] = 0x0000008c, + [R_K230_DDRC_INIT0] = 0x0002004e, + [R_K230_DDRC_INIT3] = 0x00000510, + [R_K230_DDRC_INIT5] = 0x00100000, + [R_K230_DDRC_DRAMTMG(0)] = 0x0f101b0f, + [R_K230_DDRC_DRAMTMG(1)] = 0x00080414, + [R_K230_DDRC_DRAMTMG(2)] = 0x0305060d, + [R_K230_DDRC_DRAMTMG(3)] = 0x00004000, + [R_K230_DDRC_DRAMTMG(4)] = 0x05040405, + [R_K230_DDRC_DRAMTMG(5)] = 0x05050403, + [R_K230_DDRC_DRAMTMG(6)] = 0x02020005, + [R_K230_DDRC_DRAMTMG(7)] = 0x00000202, + [R_K230_DDRC_DRAMTMG(8)] = 0x03034405, + [R_K230_DDRC_DRAMTMG(9)] = 0x0004040d, + [R_K230_DDRC_DRAMTMG(10)] = 0x001c180a, + [R_K230_DDRC_DRAMTMG(11)] = 0x440c021c, + [R_K230_DDRC_DRAMTMG(12)] = 0x00020610, + [R_K230_DDRC_DRAMTMG(13)] = 0x1c200004, + [R_K230_DDRC_DRAMTMG(14)] = 0x000000a0, + [R_K230_DDRC_DRAMTMG(16)] = 0x05100404, + [R_K230_DDRC_ZQCTL0] = 0x02000040, + [R_K230_DDRC_ZQCTL1] = 0x02000100, + [R_K230_DDRC_DFITMG0] = 0x07020002, + [R_K230_DDRC_DFITMG1] = 0x00000404, + [R_K230_DDRC_DFIUPD0] = 0x00400003, + [R_K230_DDRC_DFIUPD1] = 0x00010001, + [R_K230_DDRC_DFIUPD2] = 0x80000000, + [R_K230_DDRC_DFIMISC] = 0x00000001, + [R_K230_DDRC_DFITMG2] = 0x00000202, + [R_K230_DDRC_ODTCFG] = 0x04000400, + [R_K230_DDRC_SWCTL] = 0x00000001, + [R_K230_DDRC_SWSTAT] = 0x00000001, +}; + +/* K230 DDR PHY CSRs use byte offset = CSR index * 4. */ +#define K230_DDR_PHY_CSR_OFFSET(index) ((index) * 4) +#define K230_DDR_PHY_MASTER_CSR(reg) \ + K230_DDR_PHY_CSR_OFFSET(0x20000 + (reg)) +#define K230_DDR_PHY_APBONLY_CSR(reg) \ + K230_DDR_PHY_CSR_OFFSET(0xd0000 + (reg)) + +#define K230_DDR_PHY_ATX_IMPEDANCE_RESET UINT32_C(0x03ff) + +REG32(K230_DDR_PHY_MICRO_CONT_MUX_SEL, + K230_DDR_PHY_APBONLY_CSR(0x000)) + FIELD(K230_DDR_PHY_MICRO_CONT_MUX_SEL, MICRO_CONT_MUX_SEL, 0, 1) + +REG32(K230_DDR_PHY_TRAINING_STATUS, + K230_DDR_PHY_APBONLY_CSR(0x004)) + FIELD(K230_DDR_PHY_TRAINING_STATUS, MAILBOX_EMPTY, 0, 1) + +REG32(K230_DDR_PHY_TRAINING_ACK, + K230_DDR_PHY_APBONLY_CSR(0x031)) + +REG32(K230_DDR_PHY_TRAINING_MESSAGE, + K230_DDR_PHY_APBONLY_CSR(0x032)) + +REG32(K230_DDR_PHY_TRAINING_TRIGGER, + K230_DDR_PHY_APBONLY_CSR(0x099)) + +#define K230_DDR_PHY_TX_IMPEDANCE_CTRL1_RESET UINT32_C(0x0fff) + +REG32(K230_DDR_PHY_DFI_INIT_COMPLETE, + K230_DDR_PHY_MASTER_CSR(0x0f9)) + FIELD(K230_DDR_PHY_DFI_INIT_COMPLETE, DFI_INIT_COMPLETE, 0, 1) + +REG32(K230_DDR_PHY_VREF_IN_GLOBAL, + K230_DDR_PHY_MASTER_CSR(0x0b2)) +#define K230_DDR_PHY_VREF_IN_GLOBAL_RESET UINT32_C(0x0200) + +static void k230_ddr_cfg_update_state(K230DDRCfgState *s) +{ + bool dfi_complete = false; + unsigned int operating_mode = K230_DDRC_MODE_INIT; + + if (s->phy) { + dfi_complete = FIELD_EX32(s->phy->dfi_init_complete, + K230_DDR_PHY_DFI_INIT_COMPLETE, + DFI_INIT_COMPLETE); + } + + ARRAY_FIELD_DP32(s->regs, K230_DDRC_DFISTAT, + DFI_INIT_COMPLETE, dfi_complete); + ARRAY_FIELD_DP32(s->regs, K230_DDRC_SWSTAT, SW_DONE_ACK, + ARRAY_FIELD_EX32(s->regs, K230_DDRC_SWCTL, SW_DONE)); + + if (dfi_complete && + ARRAY_FIELD_EX32(s->regs, K230_DDRC_DFIMISC, + DFI_INIT_COMPLETE_EN)) { + s->initialized = true; + } + + if (s->initialized) { + operating_mode = K230_DDRC_MODE_NORMAL; + } + + ARRAY_FIELD_DP32(s->regs, K230_DDRC_STAT, + OPERATING_MODE, operating_mode); +} + +static uint64_t k230_ddr_cfg_read(void *opaque, hwaddr addr, unsigned int size) +{ + K230DDRCfgState *s = opaque; + uint64_t value = 0; + + if (addr < K230_DDRC_REG_SIZE) { + k230_ddr_cfg_update_state(s); + value = s->regs[addr / sizeof(uint32_t)]; + } + + return value; +} + +static void k230_ddr_cfg_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + K230DDRCfgState *s = opaque; + uint32_t val = value; + + if (addr >= K230_DDRC_REG_SIZE) { + return; + } + + switch (addr) { + case A_K230_DDRC_STAT: + case A_K230_DDRC_DFISTAT: + case A_K230_DDRC_ZQSTAT: + case A_K230_DDRC_SWSTAT: + return; + case A_K230_DDRC_PWRCTL: + s->regs[R_K230_DDRC_PWRCTL] = val & MAKE_64BIT_MASK(0, 9); + break; + case A_K230_DDRC_ZQCTL2: + /* ZQ reset completes immediately and the W1S bit self-clears. */ + s->regs[R_K230_DDRC_ZQCTL2] = 0; + s->regs[R_K230_DDRC_ZQSTAT] = 0; + break; + case A_K230_DDRC_DFIMISC: + s->regs[R_K230_DDRC_DFIMISC] = + val & (MAKE_64BIT_MASK(0, 6) | MAKE_64BIT_MASK(8, 5)); + if (s->phy && s->phy->training_complete && + FIELD_EX32(val, K230_DDRC_DFIMISC, DFI_INIT_START)) { + s->phy->dfi_init_complete = FIELD_DP32( + s->phy->dfi_init_complete, + K230_DDR_PHY_DFI_INIT_COMPLETE, DFI_INIT_COMPLETE, 1); + } + break; + case A_K230_DDRC_SWCTL: + s->regs[R_K230_DDRC_SWCTL] = + val & R_K230_DDRC_SWCTL_SW_DONE_MASK; + break; + default: + s->regs[addr / sizeof(uint32_t)] = val; + break; + } + + k230_ddr_cfg_update_state(s); +} + +static bool k230_ddr_phy_decode_anib(hwaddr addr, unsigned int *anib) +{ + uint32_t csr = addr / sizeof(uint32_t); + + if ((csr & 0xfff) != 0x043) { + return false; + } + + *anib = csr >> 12; + return *anib < K230_DDR_PHY_ANIB_COUNT; +} + +static bool k230_ddr_phy_decode_dbyte(hwaddr addr, uint32_t reg, + unsigned int *dbyte, + unsigned int *nibble) +{ + uint32_t csr = addr / sizeof(uint32_t); + uint32_t block_offset; + + if (csr < 0x10000) { + return false; + } + + csr -= 0x10000; + *dbyte = csr >> 12; + if (*dbyte >= K230_DDR_PHY_DBYTE_COUNT) { + return false; + } + + block_offset = csr & 0xfff; + *nibble = block_offset >> 8; + return *nibble < K230_DDR_PHY_NIBBLES_PER_DBYTE && + (block_offset & 0xff) == reg; +} + +static uint64_t k230_ddr_phy_read(void *opaque, hwaddr addr, + unsigned int size) +{ + K230DDRPhyState *s = opaque; + unsigned int dbyte; + unsigned int nibble; + unsigned int anib; + uint64_t value = 0; + + switch (addr) { + case A_K230_DDR_PHY_MICRO_CONT_MUX_SEL: + value = s->micro_cont_mux_sel; + break; + case A_K230_DDR_PHY_TRAINING_STATUS: + value = s->mailbox_message_pending ? 0 : + R_K230_DDR_PHY_TRAINING_STATUS_MAILBOX_EMPTY_MASK; + break; + case A_K230_DDR_PHY_TRAINING_MESSAGE: + value = s->training_complete ? 0x07 : 0; + break; + case A_K230_DDR_PHY_TRAINING_ACK: + case A_K230_DDR_PHY_TRAINING_TRIGGER: + break; + case A_K230_DDR_PHY_DFI_INIT_COMPLETE: + value = s->dfi_init_complete; + break; + case A_K230_DDR_PHY_VREF_IN_GLOBAL: + value = s->vref_in_global; + break; + default: + if (k230_ddr_phy_decode_anib(addr, &anib)) { + value = s->atx_impedance[anib]; + } else if (k230_ddr_phy_decode_dbyte(addr, 0x049, + &dbyte, &nibble)) { + value = s->tx_impedance_ctrl1[dbyte][nibble]; + } else if (k230_ddr_phy_decode_dbyte(addr, 0x04d, + &dbyte, &nibble)) { + value = s->tx_odt_drv_stren[dbyte][nibble]; + } else { + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented DDR PHY read at offset 0x%" + HWADDR_PRIx ", returning 0\n", + __func__, addr); + } + break; + } + + return value; +} + +static void k230_ddr_phy_write(void *opaque, hwaddr addr, uint64_t value, + unsigned int size) +{ + K230DDRPhyState *s = opaque; + uint16_t val = value; + unsigned int dbyte; + unsigned int nibble; + unsigned int anib; + + switch (addr) { + case A_K230_DDR_PHY_MICRO_CONT_MUX_SEL: + s->micro_cont_mux_sel = + val & R_K230_DDR_PHY_MICRO_CONT_MUX_SEL_MICRO_CONT_MUX_SEL_MASK; + return; + case A_K230_DDR_PHY_TRAINING_STATUS: + case A_K230_DDR_PHY_TRAINING_MESSAGE: + return; + case A_K230_DDR_PHY_TRAINING_ACK: + if (val == 0) { + s->mailbox_message_pending = false; + } + return; + case A_K230_DDR_PHY_TRAINING_TRIGGER: + if (val != 0) { + s->training_trigger_seen = true; + } else if (s->training_trigger_seen) { + /* Abstract the loaded firmware run to its final mailbox message. */ + s->training_trigger_seen = false; + s->training_complete = true; + s->mailbox_message_pending = true; + } + return; + default: + break; + } + + if (FIELD_EX32(s->micro_cont_mux_sel, + K230_DDR_PHY_MICRO_CONT_MUX_SEL, + MICRO_CONT_MUX_SEL)) { + return; + } + + switch (addr) { + case A_K230_DDR_PHY_DFI_INIT_COMPLETE: + s->dfi_init_complete = + val & R_K230_DDR_PHY_DFI_INIT_COMPLETE_DFI_INIT_COMPLETE_MASK; + return; + case A_K230_DDR_PHY_VREF_IN_GLOBAL: + s->vref_in_global = val & UINT16_C(0x7fff); + return; + default: + break; + } + + if (k230_ddr_phy_decode_anib(addr, &anib)) { + s->atx_impedance[anib] = val & UINT16_C(0x03ff); + return; + } + + if (k230_ddr_phy_decode_dbyte(addr, 0x049, &dbyte, &nibble)) { + s->tx_impedance_ctrl1[dbyte][nibble] = val & UINT16_C(0x0fff); + return; + } + + if (k230_ddr_phy_decode_dbyte(addr, 0x04d, &dbyte, &nibble)) { + s->tx_odt_drv_stren[dbyte][nibble] = val & UINT16_C(0x0fff); + return; + } + + qemu_log_mask(LOG_UNIMP, + "%s: unimplemented DDR PHY write at offset 0x%" + HWADDR_PRIx ", value 0x%" PRIx64 "\n", + __func__, addr, value); +} + +static const MemoryRegionOps k230_ddr_cfg_ops = { + .read = k230_ddr_cfg_read, + .write = k230_ddr_cfg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static const MemoryRegionOps k230_ddr_phy_ops = { + .read = k230_ddr_phy_read, + .write = k230_ddr_phy_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void k230_ddr_cfg_init(Object *obj) +{ + K230DDRCfgState *s = K230_DDR_CFG(obj); + + memory_region_init_io(&s->mmio, obj, &k230_ddr_cfg_ops, s, + "k230-ddr-cfg", K230_DDRC_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void k230_ddr_phy_init(Object *obj) +{ + K230DDRPhyState *s = K230_DDR_PHY(obj); + + memory_region_init_io(&s->mmio, obj, &k230_ddr_phy_ops, s, + "k230-ddr-phy", K230_DDR_PHY_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void k230_ddr_cfg_reset_enter(Object *obj, ResetType type) +{ + K230DDRCfgState *s = K230_DDR_CFG(obj); + + s->initialized = false; + memcpy(s->regs, k230_ddrc_reset_values, sizeof(s->regs)); +} + +static void k230_ddr_phy_reset_enter(Object *obj, ResetType type) +{ + K230DDRPhyState *s = K230_DDR_PHY(obj); + unsigned int dbyte; + unsigned int nibble; + unsigned int anib; + + memset(s->tx_odt_drv_stren, 0, sizeof(s->tx_odt_drv_stren)); + + for (anib = 0; anib < ARRAY_SIZE(s->atx_impedance); anib++) { + s->atx_impedance[anib] = K230_DDR_PHY_ATX_IMPEDANCE_RESET; + } + + for (dbyte = 0; dbyte < ARRAY_SIZE(s->tx_impedance_ctrl1); dbyte++) { + for (nibble = 0; + nibble < ARRAY_SIZE(s->tx_impedance_ctrl1[dbyte]); nibble++) { + s->tx_impedance_ctrl1[dbyte][nibble] = + K230_DDR_PHY_TX_IMPEDANCE_CTRL1_RESET; + } + } + + s->dfi_init_complete = 0; + s->vref_in_global = K230_DDR_PHY_VREF_IN_GLOBAL_RESET; + s->micro_cont_mux_sel = 0; + s->training_trigger_seen = false; + s->training_complete = false; + s->mailbox_message_pending = false; +} + +static const VMStateDescription vmstate_k230_ddr_cfg = { + .name = "k230-ddr-cfg", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, K230DDRCfgState, K230_DDRC_REG_COUNT), + VMSTATE_BOOL(initialized, K230DDRCfgState), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_k230_ddr_phy = { + .name = "k230-ddr-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT16_ARRAY(atx_impedance, K230DDRPhyState, + K230_DDR_PHY_ANIB_COUNT), + VMSTATE_UINT16_2DARRAY(tx_impedance_ctrl1, K230DDRPhyState, + K230_DDR_PHY_DBYTE_COUNT, + K230_DDR_PHY_NIBBLES_PER_DBYTE), + VMSTATE_UINT16_2DARRAY(tx_odt_drv_stren, K230DDRPhyState, + K230_DDR_PHY_DBYTE_COUNT, + K230_DDR_PHY_NIBBLES_PER_DBYTE), + VMSTATE_UINT16(dfi_init_complete, K230DDRPhyState), + VMSTATE_UINT16(vref_in_global, K230DDRPhyState), + VMSTATE_UINT16(micro_cont_mux_sel, K230DDRPhyState), + VMSTATE_BOOL(training_trigger_seen, K230DDRPhyState), + VMSTATE_BOOL(training_complete, K230DDRPhyState), + VMSTATE_BOOL(mailbox_message_pending, K230DDRPhyState), + VMSTATE_END_OF_LIST() + }, +}; + +static void k230_ddr_cfg_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = k230_ddr_cfg_reset_enter; + dc->vmsd = &vmstate_k230_ddr_cfg; +} + +static void k230_ddr_phy_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = k230_ddr_phy_reset_enter; + dc->vmsd = &vmstate_k230_ddr_phy; +} + +static const TypeInfo k230_ddr_types[] = { + { + .name = TYPE_K230_DDR_CFG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230DDRCfgState), + .instance_init = k230_ddr_cfg_init, + .class_init = k230_ddr_cfg_class_init, + }, + { + .name = TYPE_K230_DDR_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230DDRPhyState), + .instance_init = k230_ddr_phy_init, + .class_init = k230_ddr_phy_class_init, + }, +}; + +DEFINE_TYPES(k230_ddr_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 858ca845a2..cbeecbf1e0 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -29,6 +29,7 @@ system_ss.add(when: 'CONFIG_IOSB', if_true: files('iosb.c')) system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) # RISC-V devices +system_ss.add(when: 'CONFIG_K230', if_true: files('k230_ddr.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dmc.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c')) diff --git a/include/hw/misc/k230_ddr.h b/include/hw/misc/k230_ddr.h new file mode 100644 index 0000000000..d912e8e1b0 --- /dev/null +++ b/include/hw/misc/k230_ddr.h @@ -0,0 +1,59 @@ +/* + * Kendryte K230 DDR controller and PHY models + * + * Device state and type declarations for the K230 DDRC CFG and K230 DDR PHY + * registers. + * + * Copyright (c) 2026 Junze Cao + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_K230_DDR_H +#define HW_MISC_K230_DDR_H + +#include "hw/core/sysbus.h" + +#define K230_DDRC_MMIO_SIZE 0x02000000 +#define K230_DDR_PHY_MMIO_SIZE 0x00400000 + +#define K230_DDRC_REG_SIZE 0x328 +#define K230_DDRC_REG_COUNT (K230_DDRC_REG_SIZE / sizeof(uint32_t)) + +#define K230_DDR_PHY_ANIB_COUNT 10 +#define K230_DDR_PHY_DBYTE_COUNT 4 +#define K230_DDR_PHY_NIBBLES_PER_DBYTE 2 + +#define TYPE_K230_DDR_CFG "riscv.k230.ddr-cfg" +OBJECT_DECLARE_SIMPLE_TYPE(K230DDRCfgState, K230_DDR_CFG) + +#define TYPE_K230_DDR_PHY "riscv.k230.ddr-phy" +OBJECT_DECLARE_SIMPLE_TYPE(K230DDRPhyState, K230_DDR_PHY) + +struct K230DDRCfgState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + K230DDRPhyState *phy; + uint32_t regs[K230_DDRC_REG_COUNT]; + bool initialized; +}; + +struct K230DDRPhyState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + uint16_t atx_impedance[K230_DDR_PHY_ANIB_COUNT]; + uint16_t tx_impedance_ctrl1[K230_DDR_PHY_DBYTE_COUNT] + [K230_DDR_PHY_NIBBLES_PER_DBYTE]; + uint16_t tx_odt_drv_stren[K230_DDR_PHY_DBYTE_COUNT] + [K230_DDR_PHY_NIBBLES_PER_DBYTE]; + uint16_t dfi_init_complete; + uint16_t vref_in_global; + uint16_t micro_cont_mux_sel; + bool training_trigger_seen; + bool training_complete; + bool mailbox_message_pending; +}; + +#endif From 4309ee8e915d37763813ad4406d7f67a0fd08324 Mon Sep 17 00:00:00 2001 From: Junze Cao Date: Fri, 7 Aug 2026 18:21:17 +0800 Subject: [PATCH 717/860] hw/riscv: Connect K230 DDR controller and PHY models Replace the unimplemented K230 DDR configuration region with the DDRC model and map the PHY model at 0x9a000000. Connect the controller to the PHY so DFI status reflects PHY training and initialization state. Suggested-by: Chao Liu Reviewed-by: Daniel Henrique Barboza Signed-off-by: Junze Cao Message-ID: <20260807-k230-ddr-v2-v2-2-c531308ae819@gmail.com> Signed-off-by: Alistair Francis --- docs/system/riscv/k230.rst | 4 ++++ hw/riscv/k230.c | 19 ++++++++++++++++--- include/hw/riscv/k230.h | 4 ++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index cea8202e55..06a819fbdb 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -20,12 +20,16 @@ The ``k230`` machine supports the following devices: * Platform-Level Interrupt Controller (PLIC) * 2 K230 Watchdog Timer * 5 UART +* K230 DDRC CFG and DDR PHY Boot options ------------ The ``k230`` machine supports K230 SDK boot through M-mode U-Boot, which then starts OpenSBI/Linux with ``bootm``. It also supports direct Linux boot. +The DDRC CFG and DDR PHY models allow the K230 SDK U-Boot SPL to complete DDR +initialization before loading the next boot stage. + K230 SDK Linux kernels use T-HEAD C9xx private MAEE page table attributes. QEMU does not implement MAEE in the generic RISC-V MMU, so such kernels need to be built with standard RISC-V PTE bits before they can boot under QEMU. diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 656f28190c..627150b463 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -97,6 +97,7 @@ static const MemMapEntry memmap[] = { [K230_DEV_SPI] = { 0x91584000, 0x00001000 }, [K230_DEV_HI_SYS_CFG] = { 0x91585000, 0x00000400 }, [K230_DEV_DDRC_CFG] = { 0x98000000, 0x02000000 }, + [K230_DEV_DDR_PHY] = { 0x9A000000, 0x00400000 }, [K230_DEV_FLASH] = { 0xC0000000, 0x08000000 }, [K230_DEV_PLIC] = { 0xF00000000, 0x00400000 }, [K230_DEV_CLINT] = { 0xF04000000, 0x00400000 }, @@ -108,6 +109,11 @@ static void k230_soc_init(Object *obj) RISCVHartArrayState *cpu0 = &s->c908_cpu; object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY); + object_initialize_child(obj, "ddr-cfg", &s->ddr_cfg, + TYPE_K230_DDR_CFG); + object_initialize_child(obj, "ddr-phy", &s->ddr_phy, + TYPE_K230_DDR_PHY); + s->ddr_cfg.phy = &s->ddr_phy; object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); @@ -158,6 +164,16 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) int c908_cpus; sysbus_realize(SYS_BUS_DEVICE(&s->c908_cpu), &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ddr_cfg), errp)) { + return; + } + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ddr_phy), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_cfg), 0, + memmap[K230_DEV_DDRC_CFG].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_phy), 0, + memmap[K230_DEV_DDR_PHY].base); c908_cpus = s->c908_cpu.num_harts; @@ -361,9 +377,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) create_unimplemented_device("hi_sys_cfg", memmap[K230_DEV_HI_SYS_CFG].base, memmap[K230_DEV_HI_SYS_CFG].size); - create_unimplemented_device("ddrc_cfg", memmap[K230_DEV_DDRC_CFG].base, - memmap[K230_DEV_DDRC_CFG].size); - create_unimplemented_device("flash", memmap[K230_DEV_FLASH].base, memmap[K230_DEV_FLASH].size); } diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 592e1c26bf..80ca16338c 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -16,6 +16,7 @@ #define HW_K230_H #include "hw/core/boards.h" +#include "hw/misc/k230_ddr.h" #include "hw/riscv/riscv_hart.h" #include "hw/watchdog/k230_wdt.h" @@ -32,6 +33,8 @@ typedef struct K230SoCState { /*< public >*/ RISCVHartArrayState c908_cpu; /* Small core */ + K230DDRCfgState ddr_cfg; + K230DDRPhyState ddr_phy; K230WdtState wdt[2]; MemoryRegion sram; MemoryRegion bootrom; @@ -112,6 +115,7 @@ enum { K230_DEV_SPI, K230_DEV_HI_SYS_CFG, K230_DEV_DDRC_CFG, + K230_DEV_DDR_PHY, K230_DEV_FLASH, K230_DEV_PLIC, K230_DEV_CLINT, From 98911b604bcd881276202c4e2dd51c68b69ed4dd Mon Sep 17 00:00:00 2001 From: Junze Cao Date: Fri, 7 Aug 2026 18:21:18 +0800 Subject: [PATCH 718/860] tests/qtest: Add K230 DDR controller tests Add qtests for DDRC and PHY reset values, register access policy, the software-update handshake, and PHY register ownership. Exercise PHY training and mailbox acknowledgement through the DFI initialization sequence. Include negative coverage to ensure DFI cannot complete before PHY training or enter Normal mode before completion is enabled. Reviewed-by: Daniel Henrique Barboza Suggested-by: Chao Liu Signed-off-by: Junze Cao Message-ID: <20260807-k230-ddr-v2-v2-3-c531308ae819@gmail.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 1 + tests/qtest/k230-ddr-test.c | 226 ++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 3 +- 3 files changed, 229 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/k230-ddr-test.c diff --git a/MAINTAINERS b/MAINTAINERS index cc0879c868..2190bc706b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1851,6 +1851,7 @@ F: include/hw/misc/k230_ddr.h F: include/hw/riscv/k230.h F: include/hw/watchdog/k230_wdt.h F: tests/functional/riscv64/test_k230.py +F: tests/qtest/k230-ddr-test.c F: tests/qtest/k230-wdt-test.c RX Machines diff --git a/tests/qtest/k230-ddr-test.c b/tests/qtest/k230-ddr-test.c new file mode 100644 index 0000000000..72362929bc --- /dev/null +++ b/tests/qtest/k230-ddr-test.c @@ -0,0 +1,226 @@ +/* + * QTest testcase for the K230 DDR controller and PHY + * + * Exercises register access, reset, PHY ownership, training mailbox, and DFI + * initialization. + * + * Copyright (c) 2026 Junze Cao + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +#define K230_DDRC_BASE UINT64_C(0x98000000) +#define K230_DDR_PHY_BASE UINT64_C(0x9a000000) + +#define K230_DDRC_MSTR 0x000 +#define K230_DDRC_STAT 0x004 +#define K230_DDRC_DRAMTMG4 0x110 +#define K230_DDRC_DFIMISC 0x1b0 +#define K230_DDRC_DFISTAT 0x1bc +#define K230_DDRC_SWCTL 0x320 +#define K230_DDRC_SWSTAT 0x324 + +#define K230_DDRC_STAT_OPERATING_MODE_MASK 0x7 +#define K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK 0x1 + +#define K230_DDR_PHY_CSR(index) \ + (K230_DDR_PHY_BASE + (uint64_t)(index) * sizeof(uint32_t)) + +#define K230_DDR_PHY_ATX_IMPEDANCE 0x00043 +#define K230_DDR_PHY_TX_IMPEDANCE_CTRL1 0x10049 +#define K230_DDR_PHY_VREF_IN_GLOBAL 0x200b2 +#define K230_DDR_PHY_MICRO_CONT_MUX_SEL 0xd0000 +#define K230_DDR_PHY_TRAINING_STATUS 0xd0004 +#define K230_DDR_PHY_TRAINING_ACK 0xd0031 +#define K230_DDR_PHY_TRAINING_MESSAGE 0xd0032 +#define K230_DDR_PHY_TRAINING_TRIGGER 0xd0099 + +static void complete_phy_training(QTestState *qts) +{ + qtest_writel(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_MICRO_CONT_MUX_SEL), 1); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 9); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 1); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 0); +} + +static void test_reset_and_register_access(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), + ==, 0x01040000); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT), + ==, 0x00000000); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4), + ==, 0x05040405); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC), + ==, 0x00000001); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWCTL), + ==, 0x00000001); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 0x00000001); + + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_ATX_IMPEDANCE)), + ==, 0x03ff); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TX_IMPEDANCE_CTRL1)), + ==, 0x0fff); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), + ==, 0x0200); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_MSTR, 0x01040008); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), + ==, 0x01040008); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_STAT, 0xffffffff); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT), + ==, 0x00000000); + + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL), + 0xffffffff); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), + ==, 0x7fff); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4, 0); + + qtest_system_reset(qts); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), + ==, 0x01040000); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4), + ==, 0x05040405); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), + ==, 0x0200); + + qtest_quit(qts); +} + +static void test_software_update_handshake(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWCTL, 0); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 0); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWCTL, 1); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 1); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT, 0); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), + ==, 1); + + qtest_quit(qts); +} + +static void test_phy_ownership(void) +{ + QTestState *qts = qtest_init("-machine k230"); + uint64_t atx = K230_DDR_PHY_CSR(K230_DDR_PHY_ATX_IMPEDANCE); + uint64_t mux = K230_DDR_PHY_CSR(K230_DDR_PHY_MICRO_CONT_MUX_SEL); + + qtest_writel(qts, atx, 0x155); + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x155); + + qtest_writel(qts, mux, 1); + qtest_writel(qts, atx, 0x2aa); + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x155); + + qtest_writel(qts, mux, 0); + qtest_writel(qts, atx, 0x2aa); + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x2aa); + + qtest_quit(qts); +} + +static void test_dfi_prerequisites(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); + + g_assert_cmphex(qtest_readl(qts, + K230_DDRC_BASE + K230_DDRC_DFISTAT) & + K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK, + ==, 0); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & + K230_DDRC_STAT_OPERATING_MODE_MASK, + ==, 0); + + qtest_quit(qts); +} + +static void test_dfi_complete_enable(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + complete_phy_training(qts); + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); + + g_assert_cmphex(qtest_readl(qts, + K230_DDRC_BASE + K230_DDRC_DFISTAT) & + K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK, + ==, 1); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & + K230_DDRC_STAT_OPERATING_MODE_MASK, + ==, 0); + + qtest_quit(qts); +} + +static void test_training_and_dfi_handshake(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + complete_phy_training(qts); + + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_STATUS)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_MESSAGE)), + ==, 0x07); + + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_ACK), 0); + g_assert_cmphex(qtest_readl(qts, + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_STATUS)), + ==, 1); + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_ACK), 1); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DFISTAT), + ==, 1); + + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0); + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 1); + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & 0x7, + ==, 1); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-ddr/reset-and-register-access", + test_reset_and_register_access); + qtest_add_func("/k230-ddr/software-update-handshake", + test_software_update_handshake); + qtest_add_func("/k230-ddr/phy-ownership", test_phy_ownership); + qtest_add_func("/k230-ddr/dfi-prerequisites", test_dfi_prerequisites); + qtest_add_func("/k230-ddr/dfi-complete-enable", + test_dfi_complete_enable); + qtest_add_func("/k230-ddr/training-and-dfi-handshake", + test_training_and_dfi_handshake); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 12ed368997..663a66bded 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -299,7 +299,8 @@ qtests_riscv64 = ['riscv-csr-test'] + \ (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? ['iommu-riscv-test'] : []) + \ - (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) + (config_all_devices.has_key('CONFIG_K230') ? + ['k230-ddr-test', 'k230-wdt-test'] : []) qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-test'] From 2a99140258feaf8bd05df5b16698dde6aec1148c Mon Sep 17 00:00:00 2001 From: Yanfeng Liu Date: Sat, 18 Jul 2026 10:16:32 +0800 Subject: [PATCH 719/860] riscv/virt: Add optional UART1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds optional UART1 to RiscV virt board if required at runtime to simplify multicore development. Note that UART0 remains default serial_hd(0) and it is: - the lowest address UART - first serial in DTB - behind /aliases/serial0 in DTB - the /chosen/stdout-path in DTB Note that UART1 is placed at different page from UART0 to support page level isolation. Signed-off-by: Yanfeng Liu Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-ID: Signed-off-by: Alistair Francis --- docs/system/riscv/virt.rst | 5 ++++- hw/riscv/virt-acpi-build.c | 12 ++++++++---- hw/riscv/virt.c | 40 ++++++++++++++++++++++++++++---------- include/hw/riscv/virt.h | 3 +++ 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/docs/system/riscv/virt.rst b/docs/system/riscv/virt.rst index 60850970ce..3c87246891 100644 --- a/docs/system/riscv/virt.rst +++ b/docs/system/riscv/virt.rst @@ -16,7 +16,7 @@ The ``virt`` machine supports the following devices: * Core Local Interruptor (CLINT) * Platform-Level Interrupt Controller (PLIC) * CFI parallel NOR flash memory -* 1 NS16550 compatible UART +* Either 1 or 2 NS16550 compatible UARTs * 1 Google Goldfish RTC * 1 SiFive Test device * 8 virtio-mmio transport devices @@ -27,6 +27,9 @@ The hypervisor extension has been enabled for the default CPU, so virtual machines with hypervisor extension can simply be used without explicitly declaring. +The second UART only exists if a backend is configured explicitly (e.g. +with a second ``-serial`` command line option). + Hardware configuration information ---------------------------------- diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 59c454f4f9..8e516ec114 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -177,11 +177,11 @@ static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count, static void acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, - uint32_t uart_irq) + uint32_t uart_irq, int uartidx) { - Aml *dev = aml_device("COM0"); + Aml *dev = aml_device("COM%d", uartidx); aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0003"))); - aml_append(dev, aml_name_decl("_UID", aml_int(0))); + aml_append(dev, aml_name_decl("_UID", aml_int(uartidx))); Aml *crs = aml_resource_template(); aml_append(crs, aml_memory32_fixed(uart_memmap->base, @@ -490,7 +490,11 @@ static void build_dsdt(GArray *table_data, memmap[VIRT_APLIC_S].size, "RSCV0002"); } - acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ); + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ, 0); + if (s->uart1_present) { + acpi_dsdt_add_uart(scope, &memmap[VIRT_UART1], UART1_IRQ, 1); + } + if (virt_is_iommu_sys_enabled(s)) { acpi_dsdt_add_iommu_sys(scope, &memmap[VIRT_IOMMU_SYS], IOMMU_SYS_IRQ); } diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 51bac47a91..d58656f70d 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -97,6 +97,8 @@ static const MemMapEntry virt_memmap[] = { [VIRT_APLIC_S] = { 0xd000000, APLIC_SIZE(VIRT_CPUS_MAX) }, [VIRT_UART0] = { 0x10000000, 0x100 }, [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, + /* UART1 supports page isolation from UART0 */ + [VIRT_UART1] = { 0x1000a000, 0x100 }, [VIRT_FW_CFG] = { 0x10100000, 0x18 }, [VIRT_FLASH] = { 0x20000000, 0x4000000 }, [VIRT_IMSIC_M] = { 0x24000000, VIRT_IMSIC_MAX_SIZE }, @@ -187,7 +189,8 @@ static void create_pcie_irq_map(RISCVVirtState *s, void *fdt, char *nodename, FDT_MAX_INT_MAP_WIDTH] = {}; uint32_t *irq_map = full_irq_map; - /* This code creates a standard swizzle of interrupts such that + /* + * This code creates a standard swizzle of interrupts such that * each device's first interrupt is based on it's PCI_SLOT number. * (See pci_swizzle_map_irq_fn()) * @@ -805,28 +808,38 @@ static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle) } static void create_fdt_uart(RISCVVirtState *s, - uint32_t irq_mmio_phandle) + uint32_t irq_mmio_phandle, int memId, int irqNo) { g_autofree char *name = NULL; MachineState *ms = MACHINE(s); name = g_strdup_printf("/soc/serial@%"HWADDR_PRIx, - s->memmap[VIRT_UART0].base); + s->memmap[memId].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", - 2, s->memmap[VIRT_UART0].base, - 2, s->memmap[VIRT_UART0].size); + 2, s->memmap[memId].base, + 2, s->memmap[memId].size); qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency", 3686400); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { - qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", UART0_IRQ); + qemu_fdt_setprop_cell(ms->fdt, name, "interrupts", irqNo); } else { - qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", UART0_IRQ, 0x4); + qemu_fdt_setprop_cells(ms->fdt, name, "interrupts", irqNo, 0x4); } - qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); - qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name); + if (VIRT_UART0 == memId) { + qemu_fdt_setprop_string(ms->fdt, "/chosen", "stdout-path", name); + qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name); + } +} + +static void create_fdt_uarts(RISCVVirtState *s, uint32_t irq_mmio_phandle) +{ + if (s->uart1_present) { + create_fdt_uart(s, irq_mmio_phandle, VIRT_UART1, UART1_IRQ); + } + create_fdt_uart(s, irq_mmio_phandle, VIRT_UART0, UART0_IRQ); } static void create_fdt_rtc(RISCVVirtState *s, @@ -996,7 +1009,7 @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_reset(s, &phandle); - create_fdt_uart(s, irq_mmio_phandle); + create_fdt_uarts(s, irq_mmio_phandle); create_fdt_rtc(s, irq_mmio_phandle); } @@ -1486,6 +1499,13 @@ static void virt_machine_init(MachineState *machine) 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); + if (serial_hd(1)) { + serial_mm_init(system_memory, s->memmap[VIRT_UART1].base, + 0, qdev_get_gpio_in(mmio_irqchip, UART1_IRQ), 399193, + serial_hd(1), DEVICE_LITTLE_ENDIAN); + s->uart1_present = true; + } + sysbus_create_simple("goldfish_rtc", s->memmap[VIRT_RTC].base, qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 36a2def410..7c862b0da2 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -59,6 +59,7 @@ struct RISCVVirtState { int aia_guests; char *oem_id; char *oem_table_id; + bool uart1_present; OnOffAuto acpi; const MemMapEntry *memmap; struct GPEXHost *gpex_host; @@ -79,6 +80,7 @@ enum { VIRT_APLIC_S, VIRT_UART0, VIRT_VIRTIO, + VIRT_UART1, VIRT_FW_CFG, VIRT_IMSIC_M, VIRT_IMSIC_S, @@ -94,6 +96,7 @@ enum { enum { UART0_IRQ = 10, RTC_IRQ = 11, + UART1_IRQ = 12, VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, PCIE_IRQ = 0x20, /* 32 to 35 */ From 54bc6c4f15f91aee9acf43c36cd3d6c91d31320e Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sat, 8 Aug 2026 14:22:21 +0800 Subject: [PATCH 720/860] hw/dma: add K230 gsdma K230 GSDMA includes SDMA (System Direct Memory Access) and GDMA (Graphic Direct Memory Access). In this patch, add SDMA for k230 board. The following features have not been implemented: 1. axi protocol related 2. channel arbitration 3. lower power mode SDMA supports transfer data between memory, in which case SDMA is the controller. It also supports transfer data to decomp_gzip and caching it through SRAM. In this case, decomp_gzip is the controller, which controls SDMA through handshake signals. According to "K230 Technical Reference Manual v0.3.1" section 2.5.2. https://download.kendryte.com/developer/k230/HDK/K230%E7%A1%AC%E4%BB%B6%E6%96%87%E6%A1%A3/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf This commit includes: - K230 SDMA (System Direct Memory Access) model (k230_gsdma.c, k230_gsdma.h) - GSDMA trace log (trace-events) - Kconfig and meson.build - update MAINTAINERS Signed-off-by: Tao Ding Reviewed-by: Chao Liu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Junze Cao Message-ID: <20260808062227.66961-2-dingtao0430@163.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 2 + hw/dma/Kconfig | 3 + hw/dma/k230_gsdma.c | 551 ++++++++++++++++++++++++++++++++++++ hw/dma/meson.build | 1 + hw/dma/trace-events | 6 + include/hw/dma/k230_gsdma.h | 130 +++++++++ 6 files changed, 693 insertions(+) create mode 100644 hw/dma/k230_gsdma.c create mode 100644 include/hw/dma/k230_gsdma.h diff --git a/MAINTAINERS b/MAINTAINERS index 2190bc706b..027db78b39 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1847,9 +1847,11 @@ F: docs/system/riscv/k230.rst F: hw/misc/k230_ddr.c F: hw/riscv/k230.c F: hw/watchdog/k230_wdt.c +F: hw/dma/k230_gsdma.c F: include/hw/misc/k230_ddr.h F: include/hw/riscv/k230.h F: include/hw/watchdog/k230_wdt.h +F: include/hw/dma/k230_gsdma.h F: tests/functional/riscv64/test_k230.py F: tests/qtest/k230-ddr-test.c F: tests/qtest/k230-wdt-test.c diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig index 98fbb1bb04..0a23e7d7b8 100644 --- a/hw/dma/Kconfig +++ b/hw/dma/Kconfig @@ -30,3 +30,6 @@ config SIFIVE_PDMA config XLNX_CSU_DMA bool select REGISTER + +config K230_GSDMA + bool \ No newline at end of file diff --git a/hw/dma/k230_gsdma.c b/hw/dma/k230_gsdma.c new file mode 100644 index 0000000000..7635cd19e8 --- /dev/null +++ b/hw/dma/k230_gsdma.c @@ -0,0 +1,551 @@ +/* + * Kendryte K230 GSDMA + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * GSDMA includes SDMA (System Direct Memory Access, K230 TRM section 10.2) and + * GDMA (Graphic Direct Memory Access, K230 TRM section 2.5.2). + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" +#include "hw/dma/k230_gsdma.h" +#include "hw/core/irq.h" +#include "trace.h" + +static void k230_gsdma_update_irq(K230GSDMAState *s) +{ + qemu_set_irq(s->irq, !!(s->dma_int_stat & ~s->dma_int_mask)); +} + +static bool k230_gsdma_decomp_enabled(K230GSDMAState *s) +{ + /* Only sdma channel 0 has decomp enable flag */ + return !!(s->channels[0].cfg & K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN); +} + +static bool k230_gsdma_read_sdma_llt(hwaddr addr, K230GSDMALLT *llt) +{ + bool ok = address_space_read(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, llt, + sizeof(*llt)) == MEMTX_OK; + + if (ok) { + trace_k230_gsdma_read_sdma_llt(addr, le32_to_cpu(llt->cfg), + le32_to_cpu(llt->src_addr), + le32_to_cpu(llt->line_size), + le32_to_cpu(llt->line_cfg), + le32_to_cpu(llt->dst_addr), + le32_to_cpu(llt->next_llt_addr)); + } else { + trace_k230_gsdma_read_sdma_llt_failed(addr); + } + + return ok; +} + +static unsigned int k230_gsdma_usr_data_size(uint32_t cfg) +{ + /* decode usr_dat_size in CH_CFG register */ + switch (extract32(cfg, K230_GSDMA_CH_CFG_USR_DATA_SIZE_SHIFT, 2)) { + case 0: + return 1; + case 1: + return 2; + default: + return 4; + } +} + +static void k230_gsdma_convert_endian(uint8_t *buf, uint32_t len, uint32_t cfg) +{ + unsigned int mode = extract32(cfg, + K230_GSDMA_CH_CFG_DAT_ENDIAN_SHIFT, 2); + unsigned int unit_size; + unsigned int offset; + + if (mode == 0) { + return; + } + + unit_size = 1U << mode; + for (offset = 0; offset + unit_size <= len; offset += unit_size) { + unsigned int i; + + for (i = 0; i < unit_size / 2; i++) { + uint8_t tmp = buf[offset + i]; + + buf[offset + i] = buf[offset + unit_size - i - 1]; + buf[offset + unit_size - i - 1] = tmp; + } + } +} + +static bool k230_gsdma_transfer_llt(K230GSDMAChannel *c, const K230GSDMALLT *llt) +{ + uint32_t llt_cfg = le32_to_cpu(llt->cfg); + hwaddr src = le32_to_cpu(llt->src_addr); + hwaddr dst = le32_to_cpu(llt->dst_addr); + uint32_t line_size = le32_to_cpu(llt->line_size); + uint32_t line_cfg = le32_to_cpu(llt->line_cfg); + /* Number of lines required for 2d mode */ + uint32_t line_num = + (llt_cfg & K230_GSDMA_LLT_2D_MODE) ? extract32(line_cfg, 0, 16) : 1; + /* Stride line size required for 2d mode */ + uint32_t line_space = extract32(line_cfg, 16, 16); + + bool dat_mode = c->cfg & K230_GSDMA_CH_CFG_DAT_MODE; + bool src_fixed = c->cfg & K230_GSDMA_CH_CFG_SRC_FIXED; + bool dst_fixed = c->cfg & K230_GSDMA_CH_CFG_DST_FIXED; + + uint8_t buf[8]; /* K230 sdma axi data width is 64-bit */ + uint8_t fill[4]; /* Usr data is 32-bit*/ + unsigned int fill_len = k230_gsdma_usr_data_size(c->cfg); + uint32_t i; + + memcpy(fill, &c->usr_data, sizeof(fill)); + + for (i = 0; i < line_num; i++) { + uint32_t remaining = line_size; + hwaddr line_src = src; + hwaddr line_dst = dst; + + while (remaining) { + uint32_t chunk = MIN(remaining, (uint32_t)sizeof(buf)); + + if (dat_mode) { + uint32_t j; + + for (j = 0; j < chunk; j++) { + buf[j] = fill[j % fill_len]; + } + } else if (address_space_read(&address_space_memory, line_src, + MEMTXATTRS_UNSPECIFIED, buf, + chunk) != MEMTX_OK) { + return false; + } + + k230_gsdma_convert_endian(buf, chunk, c->cfg); + + if (address_space_write(&address_space_memory, line_dst, + MEMTXATTRS_UNSPECIFIED, buf, + chunk) != MEMTX_OK) { + return false; + } + + if (!dat_mode && !src_fixed) { + line_src += chunk; + } + if (!dst_fixed) { + line_dst += chunk; + } + remaining -= chunk; + } + + if (!dat_mode && !src_fixed) { + if (llt_cfg & K230_GSDMA_LLT_2D_MODE) { /* 2d mode */ + src += line_size + line_space; + } else { + src += line_size; + } + } + + if (!dst_fixed) { + dst += line_size; + } + } + + return true; +} + +static void k230_gsdma_sdma_set_idle(K230GSDMAChannel *c) +{ + c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY | K230_GSDMA_SDMA_STATUS_PAUSE); + c->next_llt = 0; +} + +static void k230_gsdma_sdma_set_paused(K230GSDMAChannel *c, hwaddr next_llt) +{ + c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY; + c->status |= K230_GSDMA_SDMA_STATUS_PAUSE; + c->next_llt = next_llt; +} + +static void k230_gsdma_run_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr) +{ + K230GSDMAChannel *c = &s->channels[ch]; + + c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE; + c->status |= K230_GSDMA_SDMA_STATUS_BUSY; + c->next_llt = 0; + c->current_llt = addr; + + while (addr) { + K230GSDMALLT llt; + uint32_t cfg; + hwaddr next; + + if (!k230_gsdma_read_sdma_llt(addr, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + cfg = le32_to_cpu(llt.cfg); + next = le32_to_cpu(llt.next_llt_addr); + c->current_llt = addr; + + if (!k230_gsdma_transfer_llt(c, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + if (cfg & K230_GSDMA_LLT_NODE_INTR) { + s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch); + } + + if (cfg & K230_GSDMA_LLT_PAUSE) { + s->dma_int_stat |= K230_GSDMA_SDMA_PAUSE_INT(ch); + k230_gsdma_sdma_set_paused(c, next); + k230_gsdma_update_irq(s); + return; + } + + addr = next; + } + + s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch); + s->dma_ch_en &= ~BIT(ch); + k230_gsdma_sdma_set_idle(c); + k230_gsdma_update_irq(s); +} + +/* Decomp gzip request step sdma */ +static void k230_gsdma_step_sdma(K230GSDMAState *s, unsigned int ch, hwaddr addr) +{ + K230GSDMAChannel *c = &s->channels[ch]; + K230GSDMALLT llt; + uint32_t cfg; + hwaddr next; + + if (!addr || !k230_gsdma_read_sdma_llt(addr, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + cfg = le32_to_cpu(llt.cfg); + next = le32_to_cpu(llt.next_llt_addr); + + c->status |= K230_GSDMA_SDMA_STATUS_BUSY; + c->status &= ~K230_GSDMA_SDMA_STATUS_PAUSE; + c->current_llt = addr; + + if (!k230_gsdma_transfer_llt(c, &llt)) { + k230_gsdma_sdma_set_idle(c); + return; + } + + if (cfg & K230_GSDMA_LLT_NODE_INTR) { + s->dma_int_stat |= K230_GSDMA_SDMA_ITEM_INT(ch); + } + + c->status &= ~K230_GSDMA_SDMA_STATUS_BUSY; + c->next_llt = next; + if (next == 0) { + s->dma_int_stat |= K230_GSDMA_SDMA_DONE_INT(ch); + s->dma_ch_en &= ~BIT(ch); + } + + k230_gsdma_update_irq(s); +} + +static const VMStateDescription vmstate_k230_gsdma_channel = { + .name = "k230.gsdma.channel", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(ctl, K230GSDMAChannel), + VMSTATE_UINT32(status, K230GSDMAChannel), + VMSTATE_UINT32(cfg, K230GSDMAChannel), + VMSTATE_UINT32(usr_data, K230GSDMAChannel), + VMSTATE_UINT32(llt_saddr, K230GSDMAChannel), + VMSTATE_UINT32(current_llt, K230GSDMAChannel), + VMSTATE_UINT32(next_llt, K230GSDMAChannel), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_k230_gsdma = { + .name = "k230.gsdma", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(dma_ch_en, K230GSDMAState), + VMSTATE_UINT32(dma_int_mask, K230GSDMAState), + VMSTATE_UINT32(dma_int_stat, K230GSDMAState), + VMSTATE_UINT32(dma_cfg, K230GSDMAState), + VMSTATE_UINT32(dma_weight, K230GSDMAState), + VMSTATE_STRUCT_ARRAY(channels, K230GSDMAState, + K230_GSDMA_NUM_SDMA_CHANNELS, 2, + vmstate_k230_gsdma_channel, K230GSDMAChannel), + VMSTATE_END_OF_LIST() + } +}; + +static uint64_t k230_gsdma_read(void *opaque, hwaddr addr, unsigned int size) +{ + K230GSDMAState *s = K230_GSDMA(opaque); + unsigned int ch; + hwaddr ch_off; + uint64_t ret = 0; + + switch (addr) { + case K230_GSDMA_DMA_CH_EN: + ret = s->dma_ch_en; + break; + case K230_GSDMA_DMA_INT_MASK: + ret = s->dma_int_mask; + break; + case K230_GSDMA_DMA_INT_STAT: + ret = s->dma_int_stat; + break; + case K230_GSDMA_DMA_CFG: + ret = s->dma_cfg; + break; + case K230_GSDMA_DMA_WEIGHT: + ret = s->dma_weight; + break; + default: + if (addr < K230_GSDMA_CH_BASE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: not implement gdma at offset 0x%" HWADDR_PRIx + "\n", __func__, addr); + } + break; + } + + ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE; + ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE; + if (ch < K230_GSDMA_NUM_SDMA_CHANNELS) { + switch (ch_off) { + case K230_GSDMA_CH_CTL: + break; + case K230_GSDMA_CH_STATUS: + ret = s->channels[ch].status; + break; + case K230_GSDMA_CH_CFG: + ret = s->channels[ch].cfg; + break; + case K230_GSDMA_CH_USR_DATA: + ret = s->channels[ch].usr_data; + break; + case K230_GSDMA_CH_LLT_SADDR: + ret = s->channels[ch].llt_saddr; + break; + case K230_GSDMA_CH_CURRENT_LLT: + ret = s->channels[ch].current_llt; + break; + default: + break; + } + } + + trace_k230_gsdma_read(addr, size, ret); + return ret; +} + +static void k230_gsdma_write_channel(K230GSDMAState *s, unsigned int ch, + hwaddr ch_off, uint32_t value) +{ + K230GSDMAChannel *c = &s->channels[ch]; + bool handshake_mode = ch < 2 && k230_gsdma_decomp_enabled(s); + + switch (ch_off) { + case K230_GSDMA_CH_CTL: + c->ctl = value; + if ((value & K230_GSDMA_CTL_STOP) != 0) { + c->started = false; + k230_gsdma_sdma_set_idle(c); + break; + } + if ((value & K230_GSDMA_CTL_RESUME) != 0 && + (c->status & K230_GSDMA_SDMA_STATUS_PAUSE) && c->next_llt != 0) { + k230_gsdma_run_sdma(s, ch, c->next_llt); + break; + } + if (handshake_mode && (value & K230_GSDMA_CTL_START) != 0) { + c->started = true; + c->status &= ~(K230_GSDMA_SDMA_STATUS_BUSY | + K230_GSDMA_SDMA_STATUS_PAUSE); + c->current_llt = 0; + c->next_llt = 0; + break; + } + if ((value & K230_GSDMA_CTL_START) != 0 && + (s->dma_ch_en & BIT(ch)) && c->llt_saddr != 0) { + c->started = true; + k230_gsdma_run_sdma(s, ch, c->llt_saddr); + } + break; + case K230_GSDMA_CH_CFG: + c->cfg = value; + break; + case K230_GSDMA_CH_USR_DATA: + c->usr_data = value; + break; + case K230_GSDMA_CH_LLT_SADDR: + c->llt_saddr = value; + break; + case K230_GSDMA_CH_CURRENT_LLT: + break; + default: + break; + } +} + +static void k230_gsdma_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + K230GSDMAState *s = K230_GSDMA(opaque); + unsigned int ch; + hwaddr ch_off; + uint32_t v = value; + + trace_k230_gsdma_write(addr, size, value); + + switch (addr) { + case K230_GSDMA_DMA_CH_EN: + s->dma_ch_en = v & K230_GSDMA_DMA_CH_EN_MASK; + return; + case K230_GSDMA_DMA_INT_MASK: + s->dma_int_mask = v; + k230_gsdma_update_irq(s); + return; + case K230_GSDMA_DMA_INT_STAT: + s->dma_int_stat &= ~v; + k230_gsdma_update_irq(s); + return; + case K230_GSDMA_DMA_CFG: + s->dma_cfg = v; + return; + case K230_GSDMA_DMA_WEIGHT: + s->dma_weight = v & 0x00ffffff; + return; + default: + break; + } + + if (addr < K230_GSDMA_CH_BASE) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: not implement gdma at offset 0x%" HWADDR_PRIx + "\n", __func__, addr); + return; + } + + ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE; + ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE; + if (ch >= K230_GSDMA_NUM_SDMA_CHANNELS) { + return; + } + + k230_gsdma_write_channel(s, ch, ch_off, v); +} + +static void k230_gsdma_handle_signal(void *opaque, int n, int level) +{ + K230GSDMAState *s = opaque; + K230GSDMAChannel *c; + hwaddr addr; + unsigned int ch; + + if (!level || n == K230_GSDMA_GPIO_DECOMP_CTRL_EN) { + return; + } + + ch = n - 1; + + c = &s->channels[ch]; + if (!k230_gsdma_decomp_enabled(s) || !(s->dma_ch_en & BIT(ch)) || !c->started) { + return; + } + + if (c->current_llt == 0) { + addr = c->llt_saddr; + } else { + addr = c->next_llt; + } + if (!addr) { + return; + } + + k230_gsdma_step_sdma(s, ch, addr); + qemu_set_irq(s->handshake_out[n - 1], 1); + qemu_set_irq(s->handshake_out[n - 1], 0); +} + +static const MemoryRegionOps k230_gsdma_ops = { + .read = k230_gsdma_read, + .write = k230_gsdma_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void k230_gsdma_reset_hold(Object *obj, ResetType type) +{ + K230GSDMAState *s = K230_GSDMA(obj); + + memset(s->channels, 0, sizeof(s->channels)); + s->dma_ch_en = 0; + s->dma_int_mask = 0; + s->dma_int_stat = 0; + s->dma_cfg = K230_GSDMA_DMA_CFG_RESET; + s->dma_weight = 0; +} + +static void k230_gsdma_realize(DeviceState *dev, Error **errp) +{ + K230GSDMAState *s = K230_GSDMA(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &k230_gsdma_ops, s, + TYPE_K230_GSDMA, K230_GSDMA_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_in(DEVICE(dev), k230_gsdma_handle_signal, + K230_GSDMA_NUM_GPIOS_IN); + qdev_init_gpio_out(DEVICE(dev), s->handshake_out, + K230_GSDMA_NUM_GPIOS_OUT); +} + +static void k230_gsdma_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = k230_gsdma_realize; + rc->phases.hold = k230_gsdma_reset_hold; + dc->vmsd = &vmstate_k230_gsdma; + dc->desc = "Kendryte K230 GSDMA"; +} + +static const TypeInfo k230_gsdma_info = { + .name = TYPE_K230_GSDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230GSDMAState), + .class_init = k230_gsdma_class_init, +}; + +static void k230_gsdma_register_types(void) +{ + type_register_static(&k230_gsdma_info); +} + +type_init(k230_gsdma_register_types) diff --git a/hw/dma/meson.build b/hw/dma/meson.build index cc7810beb8..45db5e7357 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -12,3 +12,4 @@ system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) system_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) system_ss.add(when: 'CONFIG_XLNX_CSU_DMA', if_true: files('xlnx_csu_dma.c')) +system_ss.add(when: 'CONFIG_K230_GSDMA', if_true: files('k230_gsdma.c')) diff --git a/hw/dma/trace-events b/hw/dma/trace-events index 4c09790f9a..a494755b5a 100644 --- a/hw/dma/trace-events +++ b/hw/dma/trace-events @@ -47,3 +47,9 @@ pl330_iomem_read(uint32_t addr, uint32_t data) "addr: 0x%08"PRIx32" data: 0x%08" # xilinx_axidma.c xilinx_axidma_loading_desc_fail(uint32_t res) "error:%u" + +# k230_gsdma.c +k230_gsdma_read(uint64_t addr, unsigned int size, uint64_t data) "K230 GSDMA read: [0x%"PRIx64"] size %u -> 0x%"PRIx64 +k230_gsdma_write(uint64_t addr, unsigned int size, uint64_t data) "K230 GSDMA write: [0x%"PRIx64"] size %u <- 0x%"PRIx64 +k230_gsdma_read_sdma_llt(uint64_t addr, uint32_t cfg, uint32_t src_addr, uint32_t line_size, uint32_t line_cfg, uint32_t dst_addr, uint32_t next_llt_addr) "K230 GSDMA read SDMA LLT: addr=0x%"PRIx64" cfg=0x%"PRIx32" src=0x%"PRIx32" line_size=0x%"PRIx32" line_cfg=0x%"PRIx32" dst=0x%"PRIx32" next=0x%"PRIx32 +k230_gsdma_read_sdma_llt_failed(uint64_t addr) "K230 GSDMA read SDMA LLT failed: addr=0x%"PRIx64 diff --git a/include/hw/dma/k230_gsdma.h b/include/hw/dma/k230_gsdma.h new file mode 100644 index 0000000000..629cce29f8 --- /dev/null +++ b/include/hw/dma/k230_gsdma.h @@ -0,0 +1,130 @@ +/* + * K230 GSDMA + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_DMA_K230_GSDMA_H +#define HW_DMA_K230_GSDMA_H + +#include "hw/core/sysbus.h" + +#define TYPE_K230_GSDMA "riscv.k230.gsdma" +OBJECT_DECLARE_SIMPLE_TYPE(K230GSDMAState, K230_GSDMA) + +#define K230_GSDMA_MMIO_SIZE 0x4000 +#define K230_GSDMA_NUM_SDMA_CHANNELS 4 + +#define K230_GSDMA_CH_STRIDE 0x30 +#define K230_GSDMA_CH_BASE 0x50 + +/* K230 SDMA request input */ +enum { + K230_GSDMA_GPIO_DECOMP_CTRL_EN, + K230_GSDMA_GPIO_DMA_WRITE_REQ, + K230_GSDMA_GPIO_DMA_READ_REQ, + K230_GSDMA_NUM_GPIOS_IN, +}; + +/* K230 SDMA ACK output */ +enum { + K230_GSDMA_GPIO_DMA_WRITE_ACK, + K230_GSDMA_GPIO_DMA_READ_ACK, + K230_GSDMA_NUM_GPIOS_OUT, +}; + +/* K230 GSDMA Registers Map */ +#define K230_GSDMA_DMA_CH_EN 0x00 +#define K230_GSDMA_DMA_INT_MASK 0x04 +#define K230_GSDMA_DMA_INT_STAT 0x08 +#define K230_GSDMA_DMA_CFG 0x0c +#define K230_GSDMA_GDMA_CTRL 0x10 +#define K230_GSDMA_GDMA_LLI_BASE 0x14 +#define K230_GSDMA_GDMA_CH_CNT0 0x18 +#define K230_GSDMA_GDMA_CH_CNT_LAST 0x34 +#define K230_GSDMA_GDMA_CURRENT_LLT 0x38 +#define K230_GSDMA_DMA_WEIGHT 0x48 + +/* K230 GSDMA SDMA Channel Register Map */ +#define K230_GSDMA_CH_CTL 0x00 +#define K230_GSDMA_CH_STATUS 0x04 +#define K230_GSDMA_CH_CFG 0x08 +#define K230_GSDMA_CH_USR_DATA 0x0c +#define K230_GSDMA_CH_LLT_SADDR 0x10 +#define K230_GSDMA_CH_CURRENT_LLT 0x14 + +/* K230 SDMA Control bit */ +#define K230_GSDMA_CTL_START BIT(0) +#define K230_GSDMA_CTL_STOP BIT(1) +#define K230_GSDMA_CTL_RESUME BIT(2) + +/* K230 SDMA States bit */ +#define K230_GSDMA_SDMA_STATUS_BUSY BIT(0) +#define K230_GSDMA_SDMA_STATUS_PAUSE BIT(1) + +/* K230 SDMA Interrupt bit */ +#define K230_GSDMA_SDMA_DONE_INT(ch) BIT(ch) +#define K230_GSDMA_SDMA_ITEM_INT(ch) BIT((ch) + 4) +#define K230_GSDMA_SDMA_PAUSE_INT(ch) BIT((ch) + 8) + + +#define K230_GSDMA_DMA_CH_EN_MASK MAKE_64BIT_MASK(0, 5) +#define K230_GSDMA_DMA_CFG_RESET 0x000007ff + +/* K230 SDMA Linked List bit */ +#define K230_GSDMA_LLT_2D_MODE BIT(28) +#define K230_GSDMA_LLT_PAUSE BIT(29) +#define K230_GSDMA_LLT_NODE_INTR BIT(30) +#define K230_GSDMA_GDMA_LLT_GCH_SHIFT 16 +#define K230_GSDMA_GDMA_LLT_GCH_MASK 0x7 +#define K230_GSDMA_CH_CFG_DAT_MODE BIT(0) +#define K230_GSDMA_CH_CFG_USR_DATA_SIZE_SHIFT 1 +#define K230_GSDMA_CH_CFG_USR_DATA_SIZE_MASK 0x3 +#define K230_GSDMA_CH_CFG_DAT_ENDIAN_SHIFT 4 +#define K230_GSDMA_CH_CFG_DAT_ENDIAN_MASK 0x3 +#define K230_GSDMA_CH_CFG_SRC_FIXED BIT(8) +#define K230_GSDMA_CH_CFG_DST_FIXED BIT(9) +#define K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN BIT(10) + +/* K230 SDMA channel state */ +typedef struct K230GSDMAChannel { + uint32_t ctl; + uint32_t status; + uint32_t cfg; + uint32_t usr_data; + uint32_t llt_saddr; + uint32_t current_llt; + uint32_t next_llt; + bool started; +} K230GSDMAChannel; + +/* K230 SDMA Linked List */ +typedef struct K230GSDMALLT { + uint32_t cfg; + uint32_t src_addr; + uint32_t line_size; + uint32_t line_cfg; + uint32_t dst_addr; + uint32_t next_llt_addr; +} K230GSDMALLT; + +/* K230 GSDMA state */ +struct K230GSDMAState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + qemu_irq handshake_out[K230_GSDMA_NUM_GPIOS_OUT]; + + uint32_t dma_ch_en; + uint32_t dma_int_mask; + uint32_t dma_int_stat; + uint32_t dma_cfg; + uint32_t dma_weight; + + K230GSDMAChannel channels[K230_GSDMA_NUM_SDMA_CHANNELS]; +}; + +#endif From dde8fbba19c19d536d323705e9700b552cf29272 Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sat, 8 Aug 2026 14:22:22 +0800 Subject: [PATCH 721/860] hw/riscv: k230: add gsdma in K230 board This commit replaces the unimplemented part of GSDMA in K230. And connect the interrupt to plic. Update K230.rst. Signed-off-by: Tao Ding Reviewed-by: Junze Cao Reviewed-by: Daniel Henrique Barboza Message-ID: <20260808062227.66961-3-dingtao0430@163.com> Signed-off-by: Alistair Francis --- docs/system/riscv/k230.rst | 1 + hw/riscv/Kconfig | 1 + hw/riscv/k230.c | 12 +++++++++--- include/hw/riscv/k230.h | 3 +++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index 06a819fbdb..5cbc0b14bc 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -21,6 +21,7 @@ The ``k230`` machine supports the following devices: * 2 K230 Watchdog Timer * 5 UART * K230 DDRC CFG and DDR PHY +* System Direct Memory Access (SDMA) Boot options ------------ diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index de37c08cae..d28ac06043 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -162,3 +162,4 @@ config K230 select SERIAL_MM select UNIMP select K230_WDT + select K230_GSDMA diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 627150b463..49c3b50c57 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -116,6 +116,7 @@ static void k230_soc_init(Object *obj) s->ddr_cfg.phy = &s->ddr_phy; object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); + object_initialize_child(obj, "k230-gsdma", &s->gsdma, TYPE_K230_GSDMA); qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908); @@ -222,6 +223,14 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[1]), 0, qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT1_IRQ)); + /* Gsdma */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gsdma), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gsdma), 0, memmap[K230_DEV_GSDMA].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gsdma), 0, + qdev_get_gpio_in(DEVICE(s->c908_plic), K230_GSDMA_IRQ)); + /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", memmap[K230_DEV_KPU_L2_CACHE].base, @@ -237,9 +246,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) memmap[K230_DEV_AI_2D_ENGINE].base, memmap[K230_DEV_AI_2D_ENGINE].size); - create_unimplemented_device("gsdma", memmap[K230_DEV_GSDMA].base, - memmap[K230_DEV_GSDMA].size); - create_unimplemented_device("dma", memmap[K230_DEV_DMA].base, memmap[K230_DEV_DMA].size); diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 80ca16338c..8879d56d98 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -19,6 +19,7 @@ #include "hw/misc/k230_ddr.h" #include "hw/riscv/riscv_hart.h" #include "hw/watchdog/k230_wdt.h" +#include "hw/dma/k230_gsdma.h" #define C908_CPU_HARTID (0) @@ -36,6 +37,7 @@ typedef struct K230SoCState { K230DDRCfgState ddr_cfg; K230DDRPhyState ddr_phy; K230WdtState wdt[2]; + K230GSDMAState gsdma; MemoryRegion sram; MemoryRegion bootrom; @@ -133,6 +135,7 @@ enum { K230_UART4_IRQ = 20, K230_WDT0_IRQ = 107, K230_WDT1_IRQ = 108, + K230_GSDMA_IRQ = 140, }; #define K230_UART_COUNT 5 From 8041d173087109d7194cf2d0cf7e76d13460354f Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sat, 8 Aug 2026 14:22:23 +0800 Subject: [PATCH 722/860] tests/qtest: add test for K230 gsdma This commit adds test cases for GSDMA. Imitate the behavior of the driver, set LLT and sdma registers. After the data transmission is completed, check the destination address data. Update MAINTAINERS for this test. Run this qtest: $ mkdir build && cd build && ../configure --target-list="riscv64-softmmu" $ QTEST_QEMU_BINARY=./qemu-system-riscv64 tests/qtest/k230-gsdma-test Signed-off-by: Tao Ding Reviewed-by: Daniel Henrique Barboza Reviewed-by: Junze Cao Message-ID: <20260808062227.66961-4-dingtao0430@163.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 3 + docs/system/riscv/k230.rst | 1 + hw/misc/Kconfig | 3 + hw/misc/k230_decomp_gzip.c | 511 +++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + hw/misc/trace-events | 8 +- include/hw/misc/k230_decomp_gzip.h | 92 ++++++ tests/qtest/k230-gsdma-test.c | 201 ++++++++++++ tests/qtest/meson.build | 2 +- 9 files changed, 816 insertions(+), 6 deletions(-) create mode 100644 hw/misc/k230_decomp_gzip.c create mode 100644 include/hw/misc/k230_decomp_gzip.h create mode 100644 tests/qtest/k230-gsdma-test.c diff --git a/MAINTAINERS b/MAINTAINERS index 027db78b39..ff804b3310 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1848,13 +1848,16 @@ F: hw/misc/k230_ddr.c F: hw/riscv/k230.c F: hw/watchdog/k230_wdt.c F: hw/dma/k230_gsdma.c +F: hw/misc/k230_decomp_gzip.c F: include/hw/misc/k230_ddr.h F: include/hw/riscv/k230.h F: include/hw/watchdog/k230_wdt.h F: include/hw/dma/k230_gsdma.h +F: include/hw/misc/k230_decomp_gzip.h F: tests/functional/riscv64/test_k230.py F: tests/qtest/k230-ddr-test.c F: tests/qtest/k230-wdt-test.c +F: tests/qtest/k230-gsdma-test.c RX Machines ----------- diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index 5cbc0b14bc..237611eddf 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -22,6 +22,7 @@ The ``k230`` machine supports the following devices: * 5 UART * K230 DDRC CFG and DDR PHY * System Direct Memory Access (SDMA) +* GZIP Decompress Engine (Decomp_gzip) Boot options ------------ diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index b8860dd3e7..46e3c03cc8 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -260,4 +260,7 @@ config XLNX_ZYNQ_DDRC config AXIADO_CLK bool +config K230_DECOMP_GZIP + bool + source macio/Kconfig diff --git a/hw/misc/k230_decomp_gzip.c b/hw/misc/k230_decomp_gzip.c new file mode 100644 index 0000000000..0d8a3b2d2e --- /dev/null +++ b/hw/misc/k230_decomp_gzip.c @@ -0,0 +1,511 @@ +/* + * Kendryte K230 GZIP decompression engine + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Decompression accelerator is mainly used to implement hardware + * GZIP decompression function. (K230 TRM section 15) + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/core/qdev-properties.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" +#include "hw/core/irq.h" +#include "hw/misc/k230_decomp_gzip.h" +#include "trace.h" + +#define GZIP_METHOD_DEFLATE 8 +#define GZIP_METHOD_VENDOR_9 9 + +#define DEFLATE_BTYPE_DYNAMIC 2 + +#define K230_DECOMP_GZIP_OUTPUT_SLOTS 4 +#define K230_DECOMP_GZIP_INPUT_SLOTS 2 + +static void k230_decomp_gzip_set_output(K230DecompGzipState *s, int line, + int level) +{ + qemu_set_irq(s->signal_out[line], level); +} + +static bool k230_decomp_gzip_read_mem(K230DecompGzipState *s, hwaddr addr, + void *buf, size_t len) +{ + MemTxResult ret = address_space_read(&address_space_memory, + s->sram_base + addr, + MEMTXATTRS_UNSPECIFIED, buf, len); + + return ret == MEMTX_OK; +} + +static bool k230_decomp_gzip_write_mem(K230DecompGzipState *s, hwaddr addr, + const void *buf, size_t len) +{ + return address_space_write(&address_space_memory, s->sram_base + addr, + MEMTXATTRS_UNSPECIFIED, buf, len) == MEMTX_OK; +} + +static hwaddr k230_decomp_gzip_output_addr(K230DecompGzipState *s) +{ + return K230_DECOMP_GZIP_SRAM_OUT_BASE + + s->output.slot * K230_DECOMP_GZIP_BLOCK_SIZE + + s->output.current_offset; +} + +static hwaddr k230_decomp_gzip_input_addr(K230DecompGzipState *s) +{ + return s->input.slot * K230_DECOMP_GZIP_BLOCK_SIZE + + K230_DECOMP_GZIP_SRAM_IN_BASE; +} + +static uint32_t k230_decomp_gzip_current_input_size(K230DecompGzipState *s) +{ + if (s->total_requested == 0) { + return 0; + } + + return ((s->total_requested - 1) % K230_DECOMP_GZIP_BLOCK_SIZE) + 1; +} + +static void k230_decomp_gzip_update_ctrl_en(K230DecompGzipState *s) +{ + int level = s->active && !!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN); + + k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN, + level); +} + +static void k230_decomp_gzip_reset_stream(K230DecompGzipState *s) +{ + if (s->zstream_inited) { + inflateEnd(&s->zs); + s->zstream_inited = false; + } + memset(&s->zs, 0, sizeof(s->zs)); +} + +static void k230_decomp_gzip_finish(K230DecompGzipState *s, bool crc_ok) +{ + s->active = false; + k230_decomp_gzip_reset_stream(s); + k230_decomp_gzip_update_ctrl_en(s); + if (crc_ok) { + s->decomp_stat |= K230_DECOMP_GZIP_STAT_CRC_OK; + } else { + s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK; + } +} + +static bool k230_decomp_gzip_load_input(K230DecompGzipState *s, + uint32_t addr, uint32_t size) +{ + s->input.current_offset = 0; + + return k230_decomp_gzip_read_mem(s, addr, s->input_buf, size); +} + +static bool k230_decomp_gzip_request_input(K230DecompGzipState *s) +{ + uint32_t total_requested = s->total_requested; + + k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ, 1); + if (s->total_requested == total_requested) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: dma write request not acknowledged\n", + TYPE_K230_DECOMP_GZIP); + return false; + } + return true; +} + +static bool k230_decomp_gzip_request_output(K230DecompGzipState *s) +{ + uint32_t slot = s->output.slot; + uint32_t current_offset = s->output.current_offset; + + k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_READ_REQ, 1); + if (s->output.slot == slot && + s->output.current_offset == current_offset) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: dma read request not acknowledged\n", + TYPE_K230_DECOMP_GZIP); + return false; + } + return true; +} + +static bool k230_decomp_gzip_init_stream(K230DecompGzipState *s) +{ + uint8_t btype = (s->input_buf[10] >> 1) & 0x3; + if (btype != DEFLATE_BTYPE_DYNAMIC) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported DEFLATE BTYPE %u\n", + TYPE_K230_DECOMP_GZIP, btype); + return false; + } + + /* The compression script for K230 will modify the third byte to 0x9 */ + if (s->input_buf[2] == GZIP_METHOD_VENDOR_9) { + s->input_buf[2] = GZIP_METHOD_DEFLATE; + } + + if (inflateInit2(&s->zs, 15 + 16) != Z_OK) { + return false; + } + + s->zstream_inited = true; + return true; +} + +static bool k230_decomp_gzip_run_inflate(K230DecompGzipState *s) +{ + uint32_t output_size = s->gzip_out_size; + uint32_t current_input_size = k230_decomp_gzip_current_input_size(s); + uint32_t avail_in; + uint32_t avail_out; + uint32_t consumed; + uint32_t produced; + int ret; + + avail_in = current_input_size - s->input.current_offset; + avail_out = K230_DECOMP_GZIP_BLOCK_SIZE - s->output.current_offset; + + s->zs.next_in = s->input_buf + s->input.current_offset; + s->zs.avail_in = avail_in; + s->zs.next_out = s->output_buf; + s->zs.avail_out = avail_out; + + ret = inflate(&s->zs, Z_NO_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) { + return false; + } + + consumed = avail_in - s->zs.avail_in; + produced = avail_out - s->zs.avail_out; + + if (produced) { + if (s->total_produced + produced > output_size || + !k230_decomp_gzip_write_mem(s, k230_decomp_gzip_output_addr(s), + s->output_buf, produced)) { + return false; + } + s->output.current_offset += produced; + s->total_produced += produced; + } + + if (consumed) { + s->input.current_offset += consumed; + } + + if (ret == Z_STREAM_END) { + s->stream_end = true; + if (s->total_produced != output_size) { + return false; + } + } + + return true; +} + +static void k230_decomp_gzip_kick(K230DecompGzipState *s) +{ + if (!s->active || s->in_kick) { + return; + } + + uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK; + s->in_kick = true; + while (s->active) { + uint32_t current_input_size = k230_decomp_gzip_current_input_size(s); + /* Output is full or last block */ + if (s->output.current_offset == K230_DECOMP_GZIP_BLOCK_SIZE || + (s->stream_end && s->output.current_offset != 0)) { + if (!k230_decomp_gzip_request_output(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + continue; + } + + if (!s->zstream_inited) { + if (!k230_decomp_gzip_request_input(s) || + !k230_decomp_gzip_init_stream(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + continue; + } + + /* Transfer finish */ + if (s->stream_end) { + k230_decomp_gzip_finish(s, + s->total_produced == s->gzip_out_size); + break; + } + + /* Input buf has has been fully decompreed, need request more */ + if (s->input.current_offset == current_input_size) { + if (s->total_requested < input_size) { + if (!k230_decomp_gzip_request_input(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + continue; + } + k230_decomp_gzip_finish(s, false); + break; + } + + /* Decompress the data, input buf or output buf are consumed in one block */ + if (!k230_decomp_gzip_run_inflate(s)) { + k230_decomp_gzip_finish(s, false); + break; + } + } + s->in_kick = false; +} + +static void k230_decomp_gzip_handle_ack(void *opaque, int n, int level) +{ + K230DecompGzipState *s = opaque; + + if (!level || !s->active) { + return; + } + + switch (n) { + case K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK: + { + uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK; + uint32_t chunk = MIN(K230_DECOMP_GZIP_BLOCK_SIZE, + input_size - s->total_requested); + + if (!k230_decomp_gzip_load_input(s, k230_decomp_gzip_input_addr(s), + chunk)) { + k230_decomp_gzip_finish(s, false); + return; + } + s->total_requested += chunk; + s->input.slot = (s->input.slot + 1) % K230_DECOMP_GZIP_INPUT_SLOTS; + break; + } + case K230_DECOMP_GZIP_GPIO_DMA_READ_ACK: + if (s->output.current_offset == 0) { + k230_decomp_gzip_finish(s, false); + return; + } + s->output.slot = (s->output.slot + 1) % K230_DECOMP_GZIP_OUTPUT_SLOTS; + s->output.current_offset = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid DMA acknowledgment signal %d\n", + TYPE_K230_DECOMP_GZIP, n); + return; + } + + k230_decomp_gzip_kick(s); +} + +static void k230_decomp_gzip_start(K230DecompGzipState *s) +{ + uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK; + + k230_decomp_gzip_reset_stream(s); + s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK; + s->total_requested = 0; + s->total_produced = 0; + s->stream_end = false; + memset(&s->input, 0, sizeof(s->input)); + memset(&s->output, 0, sizeof(s->output)); + + if (!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN) || + input_size == 0 || s->gzip_out_size == 0) { + k230_decomp_gzip_finish(s, false); + return; + } + + s->active = true; + k230_decomp_gzip_update_ctrl_en(s); + k230_decomp_gzip_kick(s); +} + +static uint64_t k230_decomp_gzip_read(void *opaque, hwaddr offset, + unsigned size) +{ + K230DecompGzipState *s = opaque; + uint64_t value = 0; + + switch (offset) { + case K230_DECOMP_GZIP_DECOMP_START: + value = s->decomp_start & ~K230_DECOMP_GZIP_START; /* start bit is write only */ + break; + case K230_DECOMP_GZIP_GZIP_SRC_SIZE: + value = s->gzip_src_size; + break; + case K230_DECOMP_GZIP_GZIP_OUT_SIZE: + value = s->gzip_out_size; + break; + case K230_DECOMP_GZIP_DECOMP_STAT: + value = s->decomp_stat; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + TYPE_K230_DECOMP_GZIP, offset); + break; + } + + trace_k230_decomp_gzip_read(offset, size, value); + return value; +} + +static void k230_decomp_gzip_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + K230DecompGzipState *s = opaque; + + trace_k230_decomp_gzip_write(offset, size, value); + + switch (offset) { + case K230_DECOMP_GZIP_DECOMP_START: + s->decomp_start = value; + if (value & K230_DECOMP_GZIP_START) { + k230_decomp_gzip_start(s); + } + break; + case K230_DECOMP_GZIP_GZIP_SRC_SIZE: + s->gzip_src_size = value; + k230_decomp_gzip_update_ctrl_en(s); + break; + case K230_DECOMP_GZIP_GZIP_OUT_SIZE: + s->gzip_out_size = value; + break; + case K230_DECOMP_GZIP_DECOMP_STAT: + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + TYPE_K230_DECOMP_GZIP, offset); + break; + } +} + +static const MemoryRegionOps k230_decomp_gzip_ops = { + .read = k230_decomp_gzip_read, + .write = k230_decomp_gzip_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static const VMStateDescription vmstate_k230_decomp_gzip_slot = { + .name = "k230.decomp-gzip.slot", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(slot, K230DecompGzipSlotState), + VMSTATE_UINT32(current_offset, K230DecompGzipSlotState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_k230_decomp_gzip = { + .name = "k230.decomp-gzip", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(decomp_start, K230DecompGzipState), + VMSTATE_UINT32(gzip_src_size, K230DecompGzipState), + VMSTATE_UINT32(gzip_out_size, K230DecompGzipState), + VMSTATE_UINT32(decomp_stat, K230DecompGzipState), + VMSTATE_STRUCT(input, K230DecompGzipState, 1, + vmstate_k230_decomp_gzip_slot, + K230DecompGzipSlotState), + VMSTATE_STRUCT(output, K230DecompGzipState, 1, + vmstate_k230_decomp_gzip_slot, + K230DecompGzipSlotState), + VMSTATE_UINT32(total_requested, K230DecompGzipState), + VMSTATE_UINT32(total_produced, K230DecompGzipState), + VMSTATE_BOOL(active, K230DecompGzipState), + VMSTATE_BOOL(in_kick, K230DecompGzipState), + VMSTATE_BOOL(zstream_inited, K230DecompGzipState), + VMSTATE_BOOL(stream_end, K230DecompGzipState), + VMSTATE_UINT8_ARRAY(input_buf, K230DecompGzipState, + K230_DECOMP_GZIP_BLOCK_SIZE), + VMSTATE_UINT8_ARRAY(output_buf, K230DecompGzipState, + K230_DECOMP_GZIP_BLOCK_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static void k230_decomp_gzip_reset_hold(Object *obj, ResetType type) +{ + K230DecompGzipState *s = K230_DECOMP_GZIP(obj); + + k230_decomp_gzip_reset_stream(s); + memset(&s->zs, 0, sizeof(s->zs)); + s->decomp_start = 0; + s->gzip_src_size = 0; + s->gzip_out_size = 0; + s->decomp_stat = 0; + memset(&s->input, 0, sizeof(s->input)); + memset(&s->output, 0, sizeof(s->output)); + s->total_requested = 0; + s->total_produced = 0; + s->active = false; + s->in_kick = false; + s->stream_end = false; +} + +static void k230_decomp_gzip_realize(DeviceState *dev, Error **errp) +{ + K230DecompGzipState *s = K230_DECOMP_GZIP(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + memory_region_init_io(&s->iomem, OBJECT(dev), &k230_decomp_gzip_ops, s, + TYPE_K230_DECOMP_GZIP, + K230_DECOMP_GZIP_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + qdev_init_gpio_in(dev, k230_decomp_gzip_handle_ack, + K230_DECOMP_GZIP_NUM_GPIOS_IN); + qdev_init_gpio_out(dev, s->signal_out, + K230_DECOMP_GZIP_NUM_GPIOS_OUT); +} + +static const Property k230_decomp_gzip_properties[] = { + DEFINE_PROP_UINT64("sram-base", K230DecompGzipState, sram_base, 0), +}; + +static void k230_decomp_gzip_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + ResettableClass *rc = RESETTABLE_CLASS(oc); + + dc->realize = k230_decomp_gzip_realize; + rc->phases.hold = k230_decomp_gzip_reset_hold; + device_class_set_props(dc, k230_decomp_gzip_properties); + dc->vmsd = &vmstate_k230_decomp_gzip; + dc->desc = "Kendryte K230 GZIP decompression engine"; +} + +static const TypeInfo k230_decomp_gzip_info = { + .name = TYPE_K230_DECOMP_GZIP, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(K230DecompGzipState), + .class_init = k230_decomp_gzip_class_init, +}; + +static void k230_decomp_gzip_register_types(void) +{ + type_register_static(&k230_decomp_gzip_info); +} + +type_init(k230_decomp_gzip_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index cbeecbf1e0..54e07aacda 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -38,6 +38,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) +system_ss.add(when: 'CONFIG_K230_DECOMP_GZIP', if_true: files('k230_decomp_gzip.c')) subdir('macio') diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 2d6d2238c5..16db4ba0cc 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -443,8 +443,6 @@ iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actu iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x" iommu_testdev_dma_armed(bool armed) "armed=%d" -# vmlaunchupdate.c -launch_update_write(void) "" -vmlaunch_reset_enter(void) "" -vm_launchupdate_finalize(void) "" -restore_host_x86_igvm(void) "" +# k230_decomp_gzip.c +k230_decomp_gzip_read(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP read: [0x%"PRIx64"] size %u -> 0x%"PRIx64 +k230_decomp_gzip_write(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP write: [0x%"PRIx64"] size %u <- 0x%"PRIx64 diff --git a/include/hw/misc/k230_decomp_gzip.h b/include/hw/misc/k230_decomp_gzip.h new file mode 100644 index 0000000000..9911af0095 --- /dev/null +++ b/include/hw/misc/k230_decomp_gzip.h @@ -0,0 +1,92 @@ +/* + * K230 Decompress Engine + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MISC_K230_DECOMP_GZIP_H +#define HW_MISC_K230_DECOMP_GZIP_H + +#include +#include "hw/core/sysbus.h" + +#define TYPE_K230_DECOMP_GZIP "riscv.k230.decomp-gzip" +OBJECT_DECLARE_SIMPLE_TYPE(K230DecompGzipState, K230_DECOMP_GZIP) + +/* K230 DECOMP GZIP ACK input */ +enum { + K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK, + K230_DECOMP_GZIP_GPIO_DMA_READ_ACK, + K230_DECOMP_GZIP_NUM_GPIOS_IN, +}; + +/* K230 DECOMP GZIP REQ output */ +enum { + K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN, + K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ, + K230_DECOMP_GZIP_GPIO_DMA_READ_REQ, + K230_DECOMP_GZIP_NUM_GPIOS_OUT, +}; + +#define K230_DECOMP_GZIP_MMIO_SIZE 0x4000 + +/* K230 DECOMP GZIP Registers map */ +/* Start decompression controller */ +#define K230_DECOMP_GZIP_DECOMP_START 0x00 +/* Source data length register */ +#define K230_DECOMP_GZIP_GZIP_SRC_SIZE 0x04 +/* Output data length register */ +#define K230_DECOMP_GZIP_GZIP_OUT_SIZE 0x08 +/* Decompress status register */ +#define K230_DECOMP_GZIP_DECOMP_STAT 0x0c + +/* Start decompression controller */ +#define K230_DECOMP_GZIP_START (1U << 0) + +#define K230_DECOMP_GZIP_CTRL_EN (1U << 31) +#define K230_DECOMP_GZIP_DMA_IN_MASK 0x7fffffffU + +#define K230_DECOMP_GZIP_STAT_CRC_OK (1U << 10) +#define K230_DECOMP_GZIP_STAT_STATE_MASK 0xfU + +#define K230_DECOMP_GZIP_BLOCK_SIZE 0x00020000 +#define K230_DECOMP_GZIP_SRAM_IN_BASE 0x80000 +#define K230_DECOMP_GZIP_SRAM_OUT_BASE 0 + +typedef struct K230DecompGzipSlotState { + uint32_t slot; /* Slot index */ + /* + * Data offset in current slot. + * First valid data in input buffer. Last valid data in output buffer. + */ + uint32_t current_offset; +} K230DecompGzipSlotState; + +struct K230DecompGzipState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + hwaddr sram_base; + qemu_irq signal_out[K230_DECOMP_GZIP_NUM_GPIOS_OUT]; + uint32_t decomp_start; + uint32_t gzip_src_size; + uint32_t gzip_out_size; + uint32_t decomp_stat; + K230DecompGzipSlotState input; /* decomp gzip ring input */ + K230DecompGzipSlotState output; /* decomp gzip ring output */ + uint32_t total_requested; + uint32_t total_produced; + bool active; + bool in_kick; + bool zstream_inited; + bool stream_end; + /* Read from ring input. */ + uint8_t input_buf[K230_DECOMP_GZIP_BLOCK_SIZE]; + /* Write to ring output. */ + uint8_t output_buf[K230_DECOMP_GZIP_BLOCK_SIZE]; + z_stream zs; +}; + +#endif diff --git a/tests/qtest/k230-gsdma-test.c b/tests/qtest/k230-gsdma-test.c new file mode 100644 index 0000000000..44d4248739 --- /dev/null +++ b/tests/qtest/k230-gsdma-test.c @@ -0,0 +1,201 @@ +/* + * QTest testcase for K230 GSDMA + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "exec/hwaddr.h" +#include "hw/dma/k230_gsdma.h" +#include "libqtest.h" + +#define K230_GSDMA_BASE 0x80800000 +#define TEST_LLT_ADDR0 0x10000 +#define TEST_LLT_ADDR1 0x10040 +#define TEST_LLT_ADDR2 0x10080 + +#define TEST_SRC_ADDR0 0x20000 +#define TEST_SRC_ADDR1 0x22000 +#define TEST_SRC_ADDR2 0x23000 +#define TEST_DST_ADDR0 0x30000 +#define TEST_DST_ADDR1 0x32000 +#define TEST_DST_ADDR2 0x33000 + +static inline uint64_t gsdma_reg(hwaddr off) +{ + return K230_GSDMA_BASE + off; +} + +static inline uint64_t gsdma_ch_reg(unsigned int ch, hwaddr off) +{ + return K230_GSDMA_BASE + K230_GSDMA_CH_BASE + + ch * K230_GSDMA_CH_STRIDE + off; +} + +static void write_sdma_llt_node_full(QTestState *qts, hwaddr addr, + uint32_t cfg, hwaddr src, + uint32_t size, hwaddr dst, + hwaddr next) +{ + qtest_writel(qts, addr + offsetof(K230GSDMALLT, cfg), cfg); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, src_addr), src); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_size), size); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_cfg), 0x1); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, dst_addr), dst); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, next_llt_addr), next); +} + +static void write_sdma_llt_node(QTestState *qts, hwaddr addr, uint32_t cfg, + uint32_t next) +{ + write_sdma_llt_node_full(qts, addr, cfg, 0x11110000, 0x200, + 0x22220000, next); +} + +static void fill_pattern(uint8_t *buf, size_t size, uint8_t seed) +{ + for (size_t i = 0; i < size; i++) { + buf[i] = seed + i * 37; + } +} + +static void test_global_registers(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CFG)), ==, + K230_GSDMA_DMA_CFG_RESET); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), 0xff); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN)), ==, + K230_GSDMA_DMA_CH_EN_MASK); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_INT_MASK), 0x12345); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_MASK)), ==, + 0x12345); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CFG), 0xa5a5a5a5); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CFG)), ==, + 0xa5a5a5a5); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_WEIGHT), 0xff55aa55); + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_WEIGHT)), ==, + 0x55aa55); + + qtest_quit(qts); +} + +static void test_sdma_pause_resume(void) +{ + uint32_t int_stat; + QTestState *qts = qtest_init("-machine k230"); + + write_sdma_llt_node(qts, TEST_LLT_ADDR0, K230_GSDMA_LLT_PAUSE, + TEST_LLT_ADDR1); + write_sdma_llt_node(qts, TEST_LLT_ADDR1, K230_GSDMA_LLT_NODE_INTR, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0)); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==, + K230_GSDMA_SDMA_STATUS_PAUSE); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR0); + + int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_PAUSE_INT(0), ==, + K230_GSDMA_SDMA_PAUSE_INT(0)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT), int_stat); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_RESUME); + + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==, + 0); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR1); + + int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==, + K230_GSDMA_SDMA_DONE_INT(0)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_ITEM_INT(0), ==, + K230_GSDMA_SDMA_ITEM_INT(0)); + + qtest_quit(qts); +} + +static void test_sdma_three_llt_copy(void) +{ + uint8_t src0[0x1000]; + uint8_t src1[1]; + uint8_t src2[511]; + uint8_t dst0[sizeof(src0)]; + uint8_t dst1[sizeof(src1)]; + uint8_t dst2[sizeof(src2)]; + uint32_t int_stat; + QTestState *qts = qtest_init("-machine k230"); + + fill_pattern(src0, sizeof(src0), 0x10); + fill_pattern(src1, sizeof(src1), 0x31); + fill_pattern(src2, sizeof(src2), 0x52); + memset(dst0, 0xa5, sizeof(dst0)); + memset(dst1, 0xa5, sizeof(dst1)); + memset(dst2, 0xa5, sizeof(dst2)); + + qtest_memwrite(qts, TEST_SRC_ADDR0, src0, sizeof(src0)); + qtest_memwrite(qts, TEST_SRC_ADDR1, src1, sizeof(src1)); + qtest_memwrite(qts, TEST_SRC_ADDR2, src2, sizeof(src2)); + qtest_memwrite(qts, TEST_DST_ADDR0, dst0, sizeof(dst0)); + qtest_memwrite(qts, TEST_DST_ADDR1, dst1, sizeof(dst1)); + qtest_memwrite(qts, TEST_DST_ADDR2, dst2, sizeof(dst2)); + + write_sdma_llt_node_full(qts, TEST_LLT_ADDR0, 0, TEST_SRC_ADDR0, + sizeof(src0), TEST_DST_ADDR0, TEST_LLT_ADDR1); + write_sdma_llt_node_full(qts, TEST_LLT_ADDR1, 0, TEST_SRC_ADDR1, + sizeof(src1), TEST_DST_ADDR1, TEST_LLT_ADDR2); + write_sdma_llt_node_full(qts, TEST_LLT_ADDR2, 0, TEST_SRC_ADDR2, + sizeof(src2), TEST_DST_ADDR2, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0)); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_STATUS)), ==, + 0); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR2); + + int_stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_DONE_INT(0), ==, + K230_GSDMA_SDMA_DONE_INT(0)); + g_assert_cmphex(int_stat & K230_GSDMA_SDMA_PAUSE_INT(0), ==, 0); + + memset(dst0, 0, sizeof(dst0)); + memset(dst1, 0, sizeof(dst1)); + memset(dst2, 0, sizeof(dst2)); + qtest_memread(qts, TEST_DST_ADDR0, dst0, sizeof(dst0)); + qtest_memread(qts, TEST_DST_ADDR1, dst1, sizeof(dst1)); + qtest_memread(qts, TEST_DST_ADDR2, dst2, sizeof(dst2)); + + g_assert_cmpmem(dst0, sizeof(dst0), src0, sizeof(src0)); + g_assert_cmpmem(dst1, sizeof(dst1), src1, sizeof(src1)); + g_assert_cmpmem(dst2, sizeof(dst2), src2, sizeof(src2)); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-gsdma/global-registers", test_global_registers); + qtest_add_func("/k230-gsdma/sdma-pause-resume", test_sdma_pause_resume); + qtest_add_func("/k230-gsdma/sdma-three-llt-copy", + test_sdma_three_llt_copy); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 663a66bded..84756ba823 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -300,7 +300,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \ config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? ['iommu-riscv-test'] : []) + \ (config_all_devices.has_key('CONFIG_K230') ? - ['k230-ddr-test', 'k230-wdt-test'] : []) + ['k230-ddr-test', 'k230-wdt-test', 'k230-gsdma-test'] : []) qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-test'] From 096ead3195a6739e6ecf51cc5781018302568e83 Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sat, 8 Aug 2026 14:22:25 +0800 Subject: [PATCH 723/860] hw/riscv: k230: add decomp gzip in K230 board This commit replaces the unimplemented part of Decomp_gzip in K230. Connect to the handshake interface of GSDMA. Signed-off-by: Tao Ding Reviewed-by: Junze Cao Reviewed-by: Daniel Henrique Barboza Message-ID: <20260808062227.66961-6-dingtao0430@163.com> Signed-off-by: Alistair Francis --- hw/riscv/Kconfig | 1 + hw/riscv/k230.c | 23 +++++++++++++++++++---- include/hw/riscv/k230.h | 2 ++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index d28ac06043..d06ac26648 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -163,3 +163,4 @@ config K230 select UNIMP select K230_WDT select K230_GSDMA + select K230_DECOMP_GZIP diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 49c3b50c57..5163eaa7ef 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -117,6 +117,8 @@ static void k230_soc_init(Object *obj) object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); object_initialize_child(obj, "k230-gsdma", &s->gsdma, TYPE_K230_GSDMA); + object_initialize_child(obj, "k230-decomp-gzip", &s->decomp_gzip, + TYPE_K230_DECOMP_GZIP); qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908); @@ -231,6 +233,23 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->gsdma), 0, qdev_get_gpio_in(DEVICE(s->c908_plic), K230_GSDMA_IRQ)); + /* Decomp gzip */ + qdev_prop_set_uint64(DEVICE(&s->decomp_gzip), "sram-base", + memmap[K230_DEV_SRAM].base); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->decomp_gzip), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->decomp_gzip), 0, + memmap[K230_DEV_DECOMP_GZIP].base); + for (int i = 0; i < K230_DECOMP_GZIP_NUM_GPIOS_OUT; i++) { + qdev_connect_gpio_out(DEVICE(&s->decomp_gzip), i, + qdev_get_gpio_in(DEVICE(&s->gsdma), i)); + } + for (int i = 0; i < K230_DECOMP_GZIP_NUM_GPIOS_IN; i++) { + qdev_connect_gpio_out(DEVICE(&s->gsdma), i, + qdev_get_gpio_in(DEVICE(&s->decomp_gzip), i)); + } + /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", memmap[K230_DEV_KPU_L2_CACHE].base, @@ -249,10 +268,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) create_unimplemented_device("dma", memmap[K230_DEV_DMA].base, memmap[K230_DEV_DMA].size); - create_unimplemented_device("decomp-gzip", - memmap[K230_DEV_DECOMP_GZIP].base, - memmap[K230_DEV_DECOMP_GZIP].size); - create_unimplemented_device("2d-engine.non-ai", memmap[K230_DEV_NON_AI_2D].base, memmap[K230_DEV_NON_AI_2D].size); diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 8879d56d98..8679d09891 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -20,6 +20,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/watchdog/k230_wdt.h" #include "hw/dma/k230_gsdma.h" +#include "hw/misc/k230_decomp_gzip.h" #define C908_CPU_HARTID (0) @@ -38,6 +39,7 @@ typedef struct K230SoCState { K230DDRPhyState ddr_phy; K230WdtState wdt[2]; K230GSDMAState gsdma; + K230DecompGzipState decomp_gzip; MemoryRegion sram; MemoryRegion bootrom; From 269f5458e078b186ec51edd256e1a1d220637b17 Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sat, 8 Aug 2026 14:22:26 +0800 Subject: [PATCH 724/860] tests/qtest: add test for K230 decomp gzip This commit adds test cases for Decomp_gzip. A compressed data segment has been pre-set, configure GSDMA and decomp_gzip. Compare the decompressed data with the original data Update MAINTAINERS for this test. Run this qtest: $ mkdir build && cd build && ../configure --target-list="riscv64-softmmu" $ QTEST_QEMU_BINARY=./qemu-system-riscv64 tests/qtest/k230-decomp-gzip-test Signed-off-by: Tao Ding Reviewed-by: Daniel Henrique Barboza Reviewed-by: Junze Cao Message-ID: <20260808062227.66961-7-dingtao0430@163.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 1 + tests/qtest/k230-decomp-gzip-test.c | 200 ++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 +- 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 tests/qtest/k230-decomp-gzip-test.c diff --git a/MAINTAINERS b/MAINTAINERS index ff804b3310..c2cc6ae0fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1858,6 +1858,7 @@ F: tests/functional/riscv64/test_k230.py F: tests/qtest/k230-ddr-test.c F: tests/qtest/k230-wdt-test.c F: tests/qtest/k230-gsdma-test.c +F: tests/qtest/k230-decomp-gzip-test.c RX Machines ----------- diff --git a/tests/qtest/k230-decomp-gzip-test.c b/tests/qtest/k230-decomp-gzip-test.c new file mode 100644 index 0000000000..e80113fd28 --- /dev/null +++ b/tests/qtest/k230-decomp-gzip-test.c @@ -0,0 +1,200 @@ +/* + * QTest testcase for K230 GZIP decompression engine + * + * Copyright (c) 2026 Tao Ding + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "libqtest.h" +#include "hw/dma/k230_gsdma.h" +#include "hw/misc/k230_decomp_gzip.h" + +#define K230_DECOMP_GZIP_BASE 0x80808000 +#define K230_GSDMA_BASE 0x80800000 +#define K230_SRAM_BASE 0x80200000 +#define TEST_LLT_ADDR0 0x01000000 +#define TEST_LLT_ADDR1 0x01000040 +#define TEST_SRC_ADDR 0x01100000 +#define TEST_DST_ADDR 0x01200000 + +#define TEST_PAYLOAD_LEN (sizeof(test_payload) - 1) + +static inline uint64_t gzip_reg(hwaddr off) +{ + return K230_DECOMP_GZIP_BASE + off; +} + +static inline uint64_t gsdma_reg(hwaddr off) +{ + return K230_GSDMA_BASE + off; +} + +static inline uint64_t gsdma_ch_reg(unsigned int ch, hwaddr off) +{ + return K230_GSDMA_BASE + K230_GSDMA_CH_BASE + + ch * K230_GSDMA_CH_STRIDE + off; +} + +static inline hwaddr k230_sram_addr(hwaddr off) +{ + return K230_SRAM_BASE + off; +} + +static inline hwaddr k230_sram_input_addr(unsigned int slot) +{ + return k230_sram_addr(K230_DECOMP_GZIP_SRAM_IN_BASE + + slot * K230_DECOMP_GZIP_BLOCK_SIZE); +} + +/* + * test_gzip_data was generated by compressing test_payload in gzip format + * using Dynamic Huffman coding. The compression method byte in the gzip + * header was then changed from the standard value 0x08 to the K230-specific + * value 0x09. + */ +static const uint8_t test_gzip_data[] = { + 0x1f, 0x8b, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x05, 0xc1, 0xb1, 0x0d, 0x00, 0x20, + 0x08, 0x04, 0xc0, 0x9e, 0x29, 0x7e, 0x04, 0x23, + 0x63, 0x38, 0x85, 0x05, 0x21, 0x14, 0x10, 0x23, + 0x5f, 0x39, 0xbd, 0x77, 0x6b, 0xea, 0x80, 0xbf, + 0x38, 0x68, 0x5e, 0xdb, 0x19, 0xe5, 0xa0, 0x35, + 0x65, 0x4d, 0x1d, 0xf0, 0x17, 0x07, 0xcd, 0x6b, + 0x3b, 0xa3, 0x1c, 0xb4, 0xa6, 0xac, 0xa9, 0x03, + 0xfe, 0xe2, 0xa0, 0x79, 0x6d, 0x67, 0x94, 0x83, + 0xd6, 0x94, 0x35, 0x75, 0xc0, 0x5f, 0x1c, 0x34, + 0xaf, 0xed, 0x8c, 0x72, 0xd0, 0x9a, 0xb2, 0xa6, + 0x0e, 0xf8, 0x8b, 0x83, 0xe6, 0xb5, 0x9d, 0x51, + 0x0e, 0x5a, 0x53, 0xd6, 0xd4, 0x01, 0x7f, 0x71, + 0xd0, 0xbc, 0xb6, 0x33, 0xca, 0x41, 0x6b, 0xca, + 0x9a, 0x3a, 0xe0, 0x2f, 0x0e, 0x9a, 0xd7, 0x76, + 0x46, 0x39, 0x68, 0x4d, 0x59, 0x53, 0x07, 0xfc, + 0xc5, 0x41, 0xf3, 0xda, 0xce, 0x28, 0x07, 0xad, + 0x29, 0x1f, 0xdb, 0x9d, 0xbc, 0xdd, 0xc8, 0x00, + 0x00, 0x00, +}; + +static const uint8_t test_payload[] = + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n" + "K230 gzip streaming test\n"; + +static void write_sdma_llt_node(QTestState *qts, hwaddr addr, uint32_t src, + uint32_t dst, uint32_t len, uint32_t next) +{ + qtest_writel(qts, addr + offsetof(K230GSDMALLT, cfg), 0); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, src_addr), src); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_size), len); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, line_cfg), 0x1); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, dst_addr), dst); + qtest_writel(qts, addr + offsetof(K230GSDMALLT, next_llt_addr), next); +} + +static void test_reset_and_rw(void) +{ + QTestState *qts = qtest_init("-machine k230"); + + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE)), + ==, 0); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_STAT)), + ==, 0); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START), + K230_DECOMP_GZIP_START); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE), 0xffffffff); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE)), + ==, 0xffffffff); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE), 0x12345678); + g_assert_cmphex(qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE)), + ==, 0x12345678); + + qtest_quit(qts); +} + +static void test_gsdma_handshake_flow(void) +{ + QTestState *qts = qtest_init("-machine k230"); + uint32_t stat; + uint8_t output[TEST_PAYLOAD_LEN]; + + qtest_memwrite(qts, TEST_SRC_ADDR, test_gzip_data, sizeof(test_gzip_data)); + write_sdma_llt_node(qts, TEST_LLT_ADDR0, TEST_SRC_ADDR, + k230_sram_input_addr(0), + sizeof(test_gzip_data), 0); + write_sdma_llt_node(qts, TEST_LLT_ADDR1, + k230_sram_addr(K230_DECOMP_GZIP_SRAM_OUT_BASE), + TEST_DST_ADDR, TEST_PAYLOAD_LEN, 0); + + qtest_writel(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN), BIT(0) | BIT(1)); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CFG), + K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN); + qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_CFG), 0); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR0); + qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_LLT_SADDR), TEST_LLT_ADDR1); + qtest_writel(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + qtest_writel(qts, gsdma_ch_reg(1, K230_GSDMA_CH_CTL), K230_GSDMA_CTL_START); + + g_assert_cmphex(qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_CH_EN)), + ==, BIT(0) | BIT(1)); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_CFG)) & + K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN, + ==, K230_GSDMA_CH0_CFG_DECOMP_CTRL_EN); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(0, K230_GSDMA_CH_LLT_SADDR)), + ==, TEST_LLT_ADDR0); + g_assert_cmphex(qtest_readl(qts, gsdma_ch_reg(1, K230_GSDMA_CH_LLT_SADDR)), + ==, TEST_LLT_ADDR1); + + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_SRC_SIZE), + K230_DECOMP_GZIP_CTRL_EN | sizeof(test_gzip_data)); + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_GZIP_OUT_SIZE), + TEST_PAYLOAD_LEN); + qtest_writel(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_START), + K230_DECOMP_GZIP_START); + + stat = qtest_readl(qts, gzip_reg(K230_DECOMP_GZIP_DECOMP_STAT)); + g_assert_cmphex(stat & K230_DECOMP_GZIP_STAT_CRC_OK, ==, + K230_DECOMP_GZIP_STAT_CRC_OK); + + g_assert_cmphex(qtest_readl(qts, + gsdma_ch_reg(0, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR0); + g_assert_cmphex(qtest_readl(qts, + gsdma_ch_reg(1, K230_GSDMA_CH_CURRENT_LLT)), + ==, TEST_LLT_ADDR1); + + stat = qtest_readl(qts, gsdma_reg(K230_GSDMA_DMA_INT_STAT)); + g_assert_cmphex(stat & K230_GSDMA_SDMA_DONE_INT(0), ==, + K230_GSDMA_SDMA_DONE_INT(0)); + g_assert_cmphex(stat & K230_GSDMA_SDMA_DONE_INT(1), ==, + K230_GSDMA_SDMA_DONE_INT(1)); + + qtest_memread(qts, TEST_DST_ADDR, output, sizeof(output)); + g_assert_cmpmem(output, sizeof(output), test_payload, TEST_PAYLOAD_LEN); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-decomp-gzip/reset-and-rw", test_reset_and_rw); + qtest_add_func("/k230-decomp-gzip/gsdma-handshake-flow", + test_gsdma_handshake_flow); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 84756ba823..739df71d8d 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -300,7 +300,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \ config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? ['iommu-riscv-test'] : []) + \ (config_all_devices.has_key('CONFIG_K230') ? - ['k230-ddr-test', 'k230-wdt-test', 'k230-gsdma-test'] : []) + ['k230-ddr-test', 'k230-wdt-test', 'k230-gsdma-test', 'k230-decomp-gzip-test'] : []) qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-test'] From b92d8c60a4cdab8562c132e6256faa930a651c76 Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sat, 8 Aug 2026 14:22:27 +0800 Subject: [PATCH 725/860] hw/riscv: k230: add a noc stub region in K230 board The NOC address was accessed in the k230_unzip driver, but it is currently not in k230. This commit uses create_unimplemented_device to preset the region. Signed-off-by: Tao Ding Reviewed-by: Junze Cao Reviewed-by: Daniel Henrique Barboza Message-ID: <20260808062227.66961-8-dingtao0430@163.com> Signed-off-by: Alistair Francis --- hw/riscv/k230.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 5163eaa7ef..558f30b97e 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -250,6 +250,9 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->decomp_gzip), i)); } + /* Noc stub, the k230 decomp_gzip driver in uboot will access this region */ + create_unimplemented_device("noc-stub", 0x91302000, 0x1000); + /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", memmap[K230_DEV_KPU_L2_CACHE].base, From 7df9aa5f6d600584c556776505253e849b3902ee Mon Sep 17 00:00:00 2001 From: Xie Bo Date: Sat, 8 Aug 2026 20:51:56 +0800 Subject: [PATCH 726/860] target/riscv/kvm: Synchronize privilege mode The KVM core register synchronization currently omits the vCPU privilege mode. As a result, env.priv can be stale when the migration stream is saved and the destination can restore the vCPU in the wrong mode. Read and write the KVM core mode register together with the other core registers. The generic RISC-V CPU VMState already carries env.priv, so no migration format change is required. Signed-off-by: Xie Bo Reviewed-by: Daniel Henrique Barboza Message-ID: <20260808125157.1220511-2-xb@ultrarisc.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 97069bf597..f6ddd0db9b 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -603,6 +603,12 @@ static int kvm_riscv_get_regs_core(CPUState *cs) } env->pc = reg; + ret = kvm_get_one_reg(cs, RISCV_CORE_REG(mode), ®); + if (ret) { + return ret; + } + env->priv = reg; + for (i = 1; i < 32; i++) { uint64_t id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, i); ret = kvm_get_one_reg(cs, id, ®); @@ -628,6 +634,12 @@ static int kvm_riscv_put_regs_core(CPUState *cs) return ret; } + reg = env->priv; + ret = kvm_set_one_reg(cs, RISCV_CORE_REG(mode), ®); + if (ret) { + return ret; + } + for (i = 1; i < 32; i++) { uint64_t id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, i); reg = env->gpr[i]; From ddac7cfb00ad771f6ef3450fff2d09e749fdbaf9 Mon Sep 17 00:00:00 2001 From: Xie Bo Date: Sat, 8 Aug 2026 20:51:57 +0800 Subject: [PATCH 727/860] target/riscv/kvm: Preserve MP state across migration RISC-V KVM initializes secondary vCPUs in KVM_MP_STATE_STOPPED, but QEMU does not save their runtime MP state. A destination therefore retains reset MP state after migration and cannot reliably resume all vCPUs. Save KVM_GET_MP_STATE in a capability-gated KVM VMState subsection and restore it on KVM_PUT_FULL_STATE. Keep the existing reset initialization path unchanged. Track whether the subsection was loaded so streams where the subsection is absent retain the destination reset behavior. Bump the RISC-V CPU VMState version and minimum version to 12 for the new pre_load hook and KVM MP-state subsection. Keep the subsection out of KVM migration streams when the host does not support the MP-state capability. Signed-off-by: Xie Bo Reviewed-by: Daniel Henrique Barboza Message-ID: <20260808125157.1220511-3-xb@ultrarisc.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 4 +++ target/riscv/kvm/kvm-cpu.c | 50 ++++++++++++++++++++++++------------ target/riscv/kvm/kvm_riscv.h | 2 +- target/riscv/machine.c | 49 +++++++++++++++++++++++++++++++++-- 4 files changed, 86 insertions(+), 19 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 376cff656f..718b66487a 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -538,6 +538,10 @@ struct CPUArchState { uint64_t kvm_timer_compare; uint64_t kvm_timer_state; uint64_t kvm_timer_frequency; + + /* KVM multiprocessor state */ + uint32_t kvm_mp_state; + bool kvm_mp_state_loaded; #endif /* CONFIG_KVM */ }; diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index f6ddd0db9b..68e1501b21 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1374,25 +1374,35 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp) return ret; } + if (cap_has_mp_state) { + struct kvm_mp_state mp_state; + + ret = kvm_vcpu_ioctl(cs, KVM_GET_MP_STATE, &mp_state); + if (ret) { + return ret; + } + RISCV_CPU(cs)->env.kvm_mp_state = mp_state.mp_state; + } + return ret; } -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state) +bool kvm_riscv_has_mp_state(void) { - if (cap_has_mp_state) { - struct kvm_mp_state mp_state = { - .mp_state = state - }; + return cap_has_mp_state; +} - int ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_MP_STATE, &mp_state); - if (ret) { - fprintf(stderr, "%s: failed to sync MP_STATE %d/%s\n", - __func__, ret, strerror(-ret)); - return -1; - } +static int kvm_riscv_put_mp_state(CPUState *cs) +{ + struct kvm_mp_state mp_state = { + .mp_state = RISCV_CPU(cs)->env.kvm_mp_state, + }; + + if (!cap_has_mp_state) { + return 0; } - return 0; + return kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state); } int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) @@ -1431,10 +1441,18 @@ int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) } if (KVM_PUT_RESET_STATE == level) { - RISCVCPU *cpu = RISCV_CPU(cs); - int state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE - : KVM_MP_STATE_STOPPED; - ret = kvm_riscv_sync_mpstate_to_kvm(cpu, state); + CPURISCVState *env = &RISCV_CPU(cs)->env; + + env->kvm_mp_state = cs->cpu_index == 0 ? KVM_MP_STATE_RUNNABLE + : KVM_MP_STATE_STOPPED; + env->kvm_mp_state_loaded = false; + ret = kvm_riscv_put_mp_state(cs); + if (ret) { + return ret; + } + } else if (KVM_PUT_FULL_STATE == level && + RISCV_CPU(cs)->env.kvm_mp_state_loaded) { + ret = kvm_riscv_put_mp_state(cs); if (ret) { return ret; } diff --git a/target/riscv/kvm/kvm_riscv.h b/target/riscv/kvm/kvm_riscv.h index b2bcd1041f..61eaa12443 100644 --- a/target/riscv/kvm/kvm_riscv.h +++ b/target/riscv/kvm/kvm_riscv.h @@ -28,7 +28,7 @@ void kvm_riscv_aia_create(MachineState *machine, uint64_t group_shift, uint64_t aplic_base, uint64_t imsic_base, uint64_t guest_num); void riscv_kvm_aplic_request(void *opaque, int irq, int level); -int kvm_riscv_sync_mpstate_to_kvm(RISCVCPU *cpu, int state); +bool kvm_riscv_has_mp_state(void); void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp); uint64_t kvm_riscv_get_timebase_frequency(RISCVCPU *cpu); diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 0ab613a298..31c49ca3e6 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -25,6 +25,9 @@ #include "exec/icount.h" #include "target/riscv/tcg/debug.h" #include "hw/riscv/machines-qom.h" +#ifdef CONFIG_KVM +#include "kvm/kvm_riscv.h" +#endif static bool pmp_needed(void *opaque) { @@ -222,6 +225,44 @@ static const VMStateDescription vmstate_kvmtimer = { VMSTATE_END_OF_LIST() } }; + +static int riscv_cpu_kvm_pre_load(void *opaque) +{ + RISCVCPU *cpu = opaque; + + cpu->env.kvm_mp_state_loaded = false; + return 0; +} + +static bool kvm_mp_state_needed(void *opaque) +{ + return kvm_enabled() && kvm_riscv_has_mp_state(); +} + +static int kvm_mp_state_post_load(void *opaque, int version_id) +{ + RISCVCPU *cpu = opaque; + CPURISCVState *env = &cpu->env; + + if (!kvm_enabled() || !kvm_riscv_has_mp_state()) { + return -ENOTSUP; + } + + env->kvm_mp_state_loaded = true; + return 0; +} + +static const VMStateDescription vmstate_kvm_mp_state = { + .name = "cpu/kvm-mp-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = kvm_mp_state_needed, + .post_load = kvm_mp_state_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(env.kvm_mp_state, RISCVCPU), + VMSTATE_END_OF_LIST() + } +}; #endif static bool debug_needed(void *opaque) @@ -457,8 +498,11 @@ static const VMStateDescription vmstate_mseccfg = { const VMStateDescription vmstate_riscv_cpu = { .name = "cpu", - .version_id = 11, - .minimum_version_id = 11, + .version_id = 12, + .minimum_version_id = 12, +#ifdef CONFIG_KVM + .pre_load = riscv_cpu_kvm_pre_load, +#endif .post_load = riscv_cpu_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64_ARRAY(env.gpr, RISCVCPU, 32), @@ -522,6 +566,7 @@ const VMStateDescription vmstate_riscv_cpu = { &vmstate_rv128, #ifdef CONFIG_KVM &vmstate_kvmtimer, + &vmstate_kvm_mp_state, #endif &vmstate_envcfg, &vmstate_debug, From 4a884ba94b43f5b2a640f4ea882655503f9ae954 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Thu, 30 Jul 2026 21:18:52 +0300 Subject: [PATCH 728/860] target/riscv: Fix PC sync in trans_sspopchk for CFI exception handling Move gen_update_pc call before conditional logic to ensure consistent PC state regardless of execution path. Previously, the host instructions generated to update the cpu_pc were only executed in the failure path when shadow stack validation failed. This created inconsistent PC synchronization. This inconsistency caused issues in CF_PCREL mode where subsequent instructions calculated wrong relative offsets from stale pc_save values, and could lead to incorrect exception return addresses. This fix ensures PC is always synchronized before any helper that might raise an exception, maintaining consistent translator state across all execution paths. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4118 Signed-off-by: Max Chou [ahshehab: rebased on current master; file moved to target/riscv/tcg/insn_trans/ and the ssp load is now 64-bit wide] Tested-by: A-Shehab Signed-off-by: A-Shehab Reviewed-by: Daniel Henrique Barboza Message-ID: <20260730181852.1622-1-ahshehab24@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc b/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc index a813232887..d47a9f9c7d 100644 --- a/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc @@ -32,6 +32,7 @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) TCGLabel *skip = gen_new_label(); uint32_t tmp = (get_xl(ctx) == MXL_RV64) ? 8 : 4; TCGv data = tcg_temp_new(); + gen_update_pc(ctx, 0); TCGv_i64 wide_addr = tcg_temp_new_i64(); tcg_gen_ld_i64(wide_addr, tcg_env, offsetof(CPURISCVState, ssp)); tcg_gen_trunc_i64_tl(addr, wide_addr); @@ -42,7 +43,6 @@ static bool trans_sspopchk(DisasContext *ctx, arg_sspopchk *a) tcg_gen_brcond_tl(TCG_COND_EQ, data, rs1, skip); tcg_gen_st8_i32(tcg_constant_i32(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 ebef6685a1a29b0b106b6ff3931ffc22c489e0ae Mon Sep 17 00:00:00 2001 From: Abhigyan Kumar <314abh@gmail.com> Date: Thu, 23 Jul 2026 19:52:54 +0530 Subject: [PATCH 729/860] target/riscv: use SXL instead of MXL for read_sstatus According to the RISC-V spec, a 64-bit system can have M-mode in 64-bit with S-mode being 32-bit (SXL bits or mstatus[35:34] being 1). In this case, read_sstatus should use SXL. QEMU doesn't allow changing the SXL bits in mstatus in M-mode. This was because of the missing MSTATUS64_SXL mask in write_mstatus. Now, both the SXL field in mstatus can be safely modified in M-mode and read_sstatus correctly uses SXL not MXL. Fixes: b550f89457 ("target/riscv: Compute mstatus.sd on demand") Signed-off-by: Abhigyan Kumar <314abh@gmail.com> Reviewed-by: Daniel Henrique Barboza Message-ID: <20260723142254.1683113-1-314abh@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index 36f2004bc5..a90449b87f 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -2067,6 +2067,11 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, } if (xl != MXL_RV32 || env->debugger) { + if ((val & MSTATUS64_SXL) != 0) { + mask |= MSTATUS64_SXL; + val = riscv_write_uxl(env, val, MSTATUS64_SXL); + } + if ((val & MSTATUS64_UXL) != 0) { mask |= MSTATUS64_UXL; val = riscv_write_uxl(env, val, MSTATUS64_UXL); @@ -4014,8 +4019,8 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno, if (riscv_cpu_cfg(env)->ext_ssdbltrp) { mask |= SSTATUS_SDT; } - /* TODO: Use SXL not MXL. */ - *val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask); + + *val = add_status_sd(riscv_cpu_sxl(env), env->mstatus & mask); return RISCV_EXCP_NONE; } From d5ab0bdefe418593a744fd0c625add607117c972 Mon Sep 17 00:00:00 2001 From: Zeng Chi Date: Fri, 24 Jul 2026 14:39:27 +0800 Subject: [PATCH 730/860] target/riscv: Fix memory leak in riscv_trigger_unrealize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In riscv_trigger_unrealize(), the per-trigger QEMUTimer objects are created in riscv_trigger_realize() using timer_new_ns(). However, unrealize only calls timer_del() to cancel them, but never frees the timer objects themselves. This results in a memory leak every time a CPU instance is unrealized (e.g., during hot-unplug or machine teardown). Fix it by replacing timer_del() with timer_free(), which internally cancels the timer and frees its memory. The separate timer_del() call is no longer needed. Fixes: 820552a92e32 ("target/riscv: dynamic alloc of debug trigger arrays") Signed-off-by: Zeng Chi Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20260724063927.3360599-1-zeng_chi911@163.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/tcg/debug.c b/target/riscv/tcg/debug.c index 3c0fe70101..5ab29f6945 100644 --- a/target/riscv/tcg/debug.c +++ b/target/riscv/tcg/debug.c @@ -1080,7 +1080,7 @@ void riscv_trigger_unrealize(CPURISCVState *env) g_free(env->cpu_watchpoint); for (int i = 0; i < env->num_triggers; i++) { - timer_del(env->itrigger_timer[i]); + timer_free(env->itrigger_timer[i]); } g_free(env->itrigger_timer); } From fbc151a66ff16153a96af8a836cd64015277972c Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 10 Aug 2026 13:56:12 +0800 Subject: [PATCH 731/860] target/riscv: Add Zicclsm CPU option and enable it for the eligible CPUs Add Zicclsm CPU option and enable it for the following eligible CPUs: - Base 32 CPU (to be backward compatible) - Base 64 CPU (to be backward compatible) - XuanTie (T-Head) C908 - Tenstorrent Ascalon - Ventana Veyron V1 - XiangShan Kunminghu - MIPS P8700 (ISA doesn't include Zicclsm, but their datasheet claims that it has unaligned load/store support in hardware) Signed-off-by: Frank Chang Reviewed-by: Max Chou Acked-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20260810055618.1175500-2-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 7 +++++++ target/riscv/cpu_cfg_fields.h.inc | 2 ++ 2 files changed, 9 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e73ff53159..b2693f4c20 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3347,6 +3347,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zicbom = true, .cfg.ext_zicbop = true, .cfg.ext_zicboz = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zicsr = true, .cfg.ext_zifencei = true, @@ -3419,6 +3420,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zicbom = true, .cfg.ext_zicbop = true, .cfg.ext_zicboz = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zicsr = true, .cfg.ext_zifencei = true, @@ -3505,6 +3507,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zbs = true, .cfg.ext_zkt = true, .cfg.ext_zbkc = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicsr = true, .cfg.ext_zifencei = true, .cfg.ext_zihintpause = true, @@ -3548,6 +3551,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zicbom = true, .cfg.ext_zicbop = true, .cfg.ext_zicboz = true, + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zicond = true, .cfg.ext_zicsr = true, @@ -3597,6 +3601,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { /* ISA extensions */ .cfg.mmu = true, + .cfg.ext_zicclsm = true, .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, .cfg.pmp = true, @@ -3656,6 +3661,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { * The RISC-V Instruction Set Manual: Volume I * Unprivileged Architecture */ + .cfg.ext_zicclsm = true, .cfg.ext_zicntr = true, .cfg.ext_zihpm = true, .cfg.ext_zihintntl = true, @@ -3712,6 +3718,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU, .priv_spec = PRIV_VERSION_1_12_0, .cfg.max_satp_mode = VM_1_10_SV48, + .cfg.ext_zicclsm = true, .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, .cfg.mmu = true, diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index 9eb47af0a7..f8c27a574f 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -139,6 +139,8 @@ BOOL_FIELD(has_priv_1_11) /* Always enabled for TCG if has_priv_1_11 */ BOOL_FIELD(ext_ziccrse) +BOOL_FIELD(ext_zicclsm) + /* Vendor-specific custom extensions */ BOOL_FIELD(ext_xtheadba) BOOL_FIELD(ext_xtheadbb) From 340a8c111dbf1f8f5524c6cbe959dad9b9af56b1 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 10 Aug 2026 13:56:13 +0800 Subject: [PATCH 732/860] hw/riscv: sifive_u: Align ROM reset vector data The SiFive U ROM reset vector data needs proper 8-byte alignment for RV64 ld instructions. The misaligned load will cause exception when Zicclsm is supported as SiFive U CPU doesn't support hardware misaligned loads and stores. Add padding to ensure start_addr and fdt_load_addr are placed at 8-byte aligned offsets and adjust the load instruction offsets to match the new data layout. Signed-off-by: Frank Chang Reviewed-by: Daniel Henrique Barboza Message-ID: <20260810055618.1175500-3-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- hw/riscv/sifive_u.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 99423ef472..57a57c96e1 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -564,19 +564,20 @@ static void sifive_u_machine_init(MachineState *machine) 0, 0, 0x00028067, /* jr t0 */ + 0x00000000, /* padding for alignment */ start_addr, /* start: .dword */ start_addr_hi32, fdt_load_addr, /* fdt_laddr: .dword */ fdt_load_addr_hi32, - 0x00000000, /* fw_dyn: */ }; + if (riscv_is_32bit(&s->soc.u_cpus)) { - reset_vec[4] = 0x0202a583; /* lw a1, 32(t0) */ - reset_vec[5] = 0x0182a283; /* lw t0, 24(t0) */ + reset_vec[4] = 0x0242a583; /* lw a1, 36(t0) */ + reset_vec[5] = 0x01c2a283; /* lw t0, 28(t0) */ } else { - reset_vec[4] = 0x0202b583; /* ld a1, 32(t0) */ - reset_vec[5] = 0x0182b283; /* ld t0, 24(t0) */ + reset_vec[4] = 0x0242b583; /* ld a1, 36(t0) */ + reset_vec[5] = 0x01c2b283; /* ld t0, 28(t0) */ } From 7ff17f127bbf08bb17d2754d43028bab79a1c709 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 10 Aug 2026 13:56:14 +0800 Subject: [PATCH 733/860] target/riscv: Support raising misaligned exceptions for scalar loads/stores When the Zicclsm extension is not enabled, raise misaligned load/store exceptions for misaligned accesses from scalar load/store instructions. Signed-off-by: Frank Chang Reviewed-by: Max Chou Reviewed-by: Alistair Francis Message-ID: <20260810055618.1175500-4-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/insn_trans/trans_rvi.c.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/tcg/insn_trans/trans_rvi.c.inc b/target/riscv/tcg/insn_trans/trans_rvi.c.inc index e47b16a7fa..cc1b5dbbad 100644 --- a/target/riscv/tcg/insn_trans/trans_rvi.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvi.c.inc @@ -413,6 +413,9 @@ static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); if (get_xl(ctx) == MXL_RV128) { out = gen_load_i128(ctx, a, memop); @@ -524,6 +527,9 @@ static bool gen_store(DisasContext *ctx, arg_sb *a, MemOp memop) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); if (get_xl(ctx) == MXL_RV128) { return gen_store_i128(ctx, a, memop); From e1322e1558ac9b2547baa2eb9b5b924220e89910 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 10 Aug 2026 13:56:15 +0800 Subject: [PATCH 734/860] target/riscv: Support raising misaligned exceptions for floating-point loads/stores When the Zicclsm extension is not enabled, raise misaligned load/store exceptions for misaligned accesses from floating-point load/store instructions (RVF, RVD, Zfh). Signed-off-by: Frank Chang Reviewed-by: Daniel Henrique Barboza Message-ID: <20260810055618.1175500-5-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/insn_trans/trans_rvd.c.inc | 6 ++++++ target/riscv/tcg/insn_trans/trans_rvf.c.inc | 6 ++++++ target/riscv/tcg/insn_trans/trans_rvzfh.c.inc | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/target/riscv/tcg/insn_trans/trans_rvd.c.inc b/target/riscv/tcg/insn_trans/trans_rvd.c.inc index 3b9a745520..1be2bee946 100644 --- a/target/riscv/tcg/insn_trans/trans_rvd.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvd.c.inc @@ -61,6 +61,9 @@ static bool trans_fld(DisasContext *ctx, arg_fld *a) memop |= MO_ATOM_IFALIGN; } memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); @@ -86,6 +89,9 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) memop |= MO_ATOM_IFALIGN; } memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); diff --git a/target/riscv/tcg/insn_trans/trans_rvf.c.inc b/target/riscv/tcg/insn_trans/trans_rvf.c.inc index e935523c93..481f7282da 100644 --- a/target/riscv/tcg/insn_trans/trans_rvf.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvf.c.inc @@ -52,6 +52,9 @@ static bool trans_flw(DisasContext *ctx, arg_flw *a) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); @@ -75,6 +78,9 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) if (ctx->cfg_ptr->ext_zama16b) { memop |= MO_ATOM_WITHIN16; } + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); addr = get_address(ctx, a->rs1, a->imm); diff --git a/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc b/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc index f36b46c211..88711fb076 100644 --- a/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc @@ -50,6 +50,9 @@ static bool trans_flh(DisasContext *ctx, arg_flh *a) REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { @@ -75,6 +78,9 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a) REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx); memop |= ctx->mo_endianness; + if (!ctx->cfg_ptr->ext_zicclsm) { + memop |= MO_ALIGN; + } decode_save_opc(ctx, 0); t0 = get_gpr(ctx, a->rs1, EXT_NONE); if (a->imm) { From 460549ba1088ee9ac5f69c0f5c42c96ebb4d1355 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 10 Aug 2026 13:56:16 +0800 Subject: [PATCH 735/860] target/riscv: Support raising misaligned exceptions for vector loads/stores When the Zicclsm extension is not enabled, raise misaligned load/store exceptions for misaligned accesses from vector load/store instructions. We will skip the host fast-path and fall back to the slow TLB-path to raise misaligned load/store exceptions for the misaligned accesses when Zicclsm extension is disabled. Signed-off-by: Frank Chang Reviewed-by: Max Chou Acked-by: Alistair Francis Message-ID: <20260810055618.1175500-6-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/insn_trans/trans_rvv.c.inc | 18 +++++- target/riscv/tcg/vector_helper.c | 65 ++++++++++++++++----- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/target/riscv/tcg/insn_trans/trans_rvv.c.inc b/target/riscv/tcg/insn_trans/trans_rvv.c.inc index 23262b1d03..a22e2cae6c 100644 --- a/target/riscv/tcg/insn_trans/trans_rvv.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvv.c.inc @@ -1191,26 +1191,38 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, * Use the helper function if either: * - vstart is not 0. */ - bool use_helper_fn = !s->vstart_eq_zero; if (!use_helper_fn) { uint32_t size = s->cfg_ptr->vlenb * nf; TCGv_i64 t8 = tcg_temp_new_i64(); MemOp atomicity = MO_ATOM_NONE; + MemOp alignment = MO_UNALN; + + /* + * If Zicclsm is disabled, require alignment based on element size. + * Use MO_ALIGN_* based on log2_esz (0 = MO_UNALN, 1 = MO_ALIGN_2, etc). + */ + if (!s->cfg_ptr->ext_zicclsm) { + alignment = log2_esz << MO_ASHIFT; + } + if (log2_esz == 0) { atomicity = MO_ATOM_NONE; } else { atomicity = MO_ATOM_IFALIGN_PAIR; } + for (int i = 0; i < size; i += 8) { TCGv addr = get_address(s, rs1, i); if (is_load) { - tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx, MO_LEUQ | atomicity); + tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx, + MO_LEUQ | atomicity | alignment); tcg_gen_st_i64(t8, tcg_env, vreg_ofs(s, vd) + i); } else { tcg_gen_ld_i64(t8, tcg_env, vreg_ofs(s, vd) + i); - tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, MO_LEUQ | atomicity); + tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, + MO_LEUQ | atomicity | alignment); } if (i == size - 8) { tcg_gen_movi_i32(cpu_vstart, 0); diff --git a/target/riscv/tcg/vector_helper.c b/target/riscv/tcg/vector_helper.c index e321ca2616..e28d8a3d9f 100644 --- a/target/riscv/tcg/vector_helper.c +++ b/target/riscv/tcg/vector_helper.c @@ -199,20 +199,34 @@ static inline void vext_set_elem_mask(void *v0, int index, ((uint64_t *)v0)[idx] = deposit64(old, pos, 1, value); } +static inline MemOpIdx vext_make_memop_idx(CPURISCVState *env, size_t size) +{ + int mmu_idx = riscv_env_mmu_index(env, false); + MemOp memop = size_memop(size) | mo_endian_env(env); + + if (!riscv_cpu_cfg(env)->ext_zicclsm) { + memop |= MO_ALIGN; + } + + return make_memop_idx(memop, mmu_idx); +} + /* elements operations for load and store */ typedef void vext_ldst_elem_fn_tlb(CPURISCVState *env, abi_ptr addr, uint32_t idx, void *vd, uintptr_t retaddr); typedef void vext_ldst_elem_fn_host(void *vd, uint32_t idx, void *host); -#define GEN_VEXT_LD_ELEM(NAME, ETYPE, H, LDSUF) \ +#define GEN_VEXT_TLB_LD_ELEM(NAME, ETYPE, H, LDSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \ uint32_t idx, void *vd, uintptr_t retaddr) \ { \ ETYPE *cur = ((ETYPE *)vd + H(idx)); \ - *cur = cpu_##LDSUF##_data_ra(env, addr, retaddr); \ + MemOpIdx oi = vext_make_memop_idx(env, sizeof(ETYPE)); \ + *cur = cpu_##LDSUF##_mmu(env, addr, oi, retaddr); \ } \ - \ + +#define GEN_VEXT_HOST_LD_ELEM(NAME, ETYPE, H, LDSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_host(void *vd, uint32_t idx, void *host) \ { \ @@ -220,20 +234,27 @@ void NAME##_host(void *vd, uint32_t idx, void *host) \ *cur = (ETYPE)LDSUF##_p(host); \ } -GEN_VEXT_LD_ELEM(lde_b, uint8_t, H1, ldub) -GEN_VEXT_LD_ELEM(lde_h, uint16_t, H2, lduw_le) -GEN_VEXT_LD_ELEM(lde_w, uint32_t, H4, ldl_le) -GEN_VEXT_LD_ELEM(lde_d, uint64_t, H8, ldq_le) +GEN_VEXT_TLB_LD_ELEM(lde_b, uint8_t, H1, ldb) +GEN_VEXT_TLB_LD_ELEM(lde_h, uint16_t, H2, ldw) +GEN_VEXT_TLB_LD_ELEM(lde_w, uint32_t, H4, ldl) +GEN_VEXT_TLB_LD_ELEM(lde_d, uint64_t, H8, ldq) -#define GEN_VEXT_ST_ELEM(NAME, ETYPE, H, STSUF) \ +GEN_VEXT_HOST_LD_ELEM(lde_b, uint8_t, H1, ldub) +GEN_VEXT_HOST_LD_ELEM(lde_h, uint16_t, H2, lduw_le) +GEN_VEXT_HOST_LD_ELEM(lde_w, uint32_t, H4, ldl_le) +GEN_VEXT_HOST_LD_ELEM(lde_d, uint64_t, H8, ldq_le) + +#define GEN_VEXT_TLB_ST_ELEM(NAME, ETYPE, H, STSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_tlb(CPURISCVState *env, abi_ptr addr, \ uint32_t idx, void *vd, uintptr_t retaddr) \ { \ ETYPE data = *((ETYPE *)vd + H(idx)); \ - cpu_##STSUF##_data_ra(env, addr, data, retaddr); \ + MemOpIdx oi = vext_make_memop_idx(env, sizeof(ETYPE)); \ + cpu_##STSUF##_mmu(env, addr, data, oi, retaddr); \ } \ - \ + +#define GEN_VEXT_HOST_ST_ELEM(NAME, ETYPE, H, STSUF) \ static inline QEMU_ALWAYS_INLINE \ void NAME##_host(void *vd, uint32_t idx, void *host) \ { \ @@ -241,10 +262,15 @@ void NAME##_host(void *vd, uint32_t idx, void *host) \ STSUF##_p(host, data); \ } -GEN_VEXT_ST_ELEM(ste_b, uint8_t, H1, stb) -GEN_VEXT_ST_ELEM(ste_h, uint16_t, H2, stw_le) -GEN_VEXT_ST_ELEM(ste_w, uint32_t, H4, stl_le) -GEN_VEXT_ST_ELEM(ste_d, uint64_t, H8, stq_le) +GEN_VEXT_TLB_ST_ELEM(ste_b, uint8_t, H1, stb) +GEN_VEXT_TLB_ST_ELEM(ste_h, uint16_t, H2, stw) +GEN_VEXT_TLB_ST_ELEM(ste_w, uint32_t, H4, stl) +GEN_VEXT_TLB_ST_ELEM(ste_d, uint64_t, H8, stq) + +GEN_VEXT_HOST_ST_ELEM(ste_b, uint8_t, H1, stb) +GEN_VEXT_HOST_ST_ELEM(ste_h, uint16_t, H2, stw_le) +GEN_VEXT_HOST_ST_ELEM(ste_w, uint32_t, H4, stl_le) +GEN_VEXT_HOST_ST_ELEM(ste_d, uint64_t, H8, stq_le) static inline QEMU_ALWAYS_INLINE void vext_continuous_ldst_tlb(CPURISCVState *env, vext_ldst_elem_fn_tlb *ldst_tlb, @@ -398,7 +424,16 @@ vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr, probe_pages(env, addr, size, ra, access_type, mmu_index, &host, &flags, true); - if (flags == 0) { + bool misaligned = addr & (esz - 1); + + /* + * Allow the host fast-pash when: + * 1. Page permission/pmp/watchpoint are checked and we have a contigous + * host mapping. + * 2. Zicclsm is enabled or load/store is not a misaligned access. + * Otherwise, we will fall back to the slow TLB-path. + */ + if (flags == 0 && (riscv_cpu_cfg(env)->ext_zicclsm || !misaligned)) { if (nf == 1) { vext_continuous_ldst_host(env, ldst_host, vd, evl, env->vstart, host, esz, is_load); From 836ffe9cfa51104a5df2802f3809cea74defd522 Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 10 Aug 2026 13:56:17 +0800 Subject: [PATCH 736/860] target/riscv: Expose Zicclsm as a CPU property and update RVA22U64 profile Update Zicclsm ISA string and expose it as a CPU property to allow user to turn on/off Zicclsm extension. In addition, Zicclsm extension is mandatory for the RVA22U64 profile. Previously, Zicclsm was enabled automatically when has_priv_1_11 was true. Now that Zicclsm has been converted to an explicit CPU option, it must be explicitly added to the RVA22U64 profile's extension list to ensure the profile remains compliant with the specification. Signed-off-by: Frank Chang Reviewed-by: Daniel Henrique Barboza Message-ID: <20260810055618.1175500-7-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b2693f4c20..d8f239e41d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -165,7 +165,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zicboz, PRIV_VERSION_1_12_0, ext_zicboz), ISA_INTERNAL_EXT_DATA_ENTRY(ziccamoa, PRIV_VERSION_1_11_0, has_priv_1_11), ISA_INTERNAL_EXT_DATA_ENTRY(ziccif, PRIV_VERSION_1_11_0, has_priv_1_11), - ISA_INTERNAL_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, has_priv_1_11), + ISA_EXT_DATA_ENTRY(zicclsm, PRIV_VERSION_1_11_0, ext_zicclsm), ISA_EXT_DATA_ENTRY(ziccrse, PRIV_VERSION_1_11_0, ext_ziccrse), ISA_EXT_DATA_ENTRY(zicfilp, PRIV_VERSION_1_12_0, ext_zicfilp), ISA_EXT_DATA_ENTRY(zicfiss, PRIV_VERSION_1_13_0, ext_zicfiss), @@ -2316,6 +2316,7 @@ static RISCVCPUProfile RVA22U64 = { CPU_CFG_OFFSET(ext_zkt), CPU_CFG_OFFSET(ext_zicntr), CPU_CFG_OFFSET(ext_zihpm), CPU_CFG_OFFSET(ext_zicbom), CPU_CFG_OFFSET(ext_zicbop), CPU_CFG_OFFSET(ext_zicboz), + CPU_CFG_OFFSET(ext_zicclsm), /* mandatory named features for this profile */ CPU_CFG_OFFSET(ext_zic64b), From efb3db26887d7c8c515048019bd71d405173a59d Mon Sep 17 00:00:00 2001 From: Frank Chang Date: Mon, 10 Aug 2026 13:56:18 +0800 Subject: [PATCH 737/860] tests/tcg/riscv64: Add test for Zicclsm Cover scalar, floating-point, vector, and segmented misaligned accesses with Zicclsm enabled and disabled. Clean up both generated test binaries. To build and run the tests: make -C build/tests/tcg/riscv64-softmmu \ CC=riscv64-unknown-elf-gcc LD=riscv64-unknown-elf-ld \ test-zicclsm test-zicclsm-off make -C build/tests/tcg/riscv64-softmmu \ run-test-zicclsm run-test-zicclsm-off To clean the generated binaries and objects: make -C build/tests/tcg/riscv64-softmmu clean Signed-off-by: Frank Chang Reviewed-by: Daniel Henrique Barboza Message-ID: <20260810055618.1175500-8-frank.chang@sifive.com> Signed-off-by: Alistair Francis --- tests/tcg/riscv64/Makefile.softmmu-target | 22 ++ tests/tcg/riscv64/test-zicclsm.S | 368 ++++++++++++++++++++++ 2 files changed, 390 insertions(+) create mode 100644 tests/tcg/riscv64/test-zicclsm.S diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index 82be8a2c91..908fd008e4 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -41,5 +41,27 @@ comma:= , run-test-crc32: test-crc32 $(call run-test, $<, $(QEMU) -cpu rv64$(comma)xlrbr=true $(QEMU_OPTS)$<) +# Zicclsm: misaligned load/store support. Assemble one source twice: the +# default build expects every misaligned access to succeed (zicclsm=true), +# the -DZICCLSM_DISABLED build expects every one to trap (zicclsm=false). +ZICCLSM_MARCH = -march=rv64gcv_zfh +CLEANFILES += test-zicclsm test-zicclsm-off + +test-zicclsm: test-zicclsm.S $(LINK_SCRIPT) + $(CC) $(CFLAGS) $(ZICCLSM_MARCH) $< -Wa,--noexecstack -c -o test-zicclsm.o + $(LD) $(LDFLAGS) test-zicclsm.o -o $@ + +test-zicclsm-off: test-zicclsm.S $(LINK_SCRIPT) + $(CC) $(CFLAGS) $(ZICCLSM_MARCH) -DZICCLSM_DISABLED $< -Wa,--noexecstack -c -o test-zicclsm-off.o + $(LD) $(LDFLAGS) test-zicclsm-off.o -o $@ + +EXTRA_RUNS += run-test-zicclsm +run-test-zicclsm: test-zicclsm + $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)zfh=true$(comma)zicclsm=true $(QEMU_OPTS)$<) + +EXTRA_RUNS += run-test-zicclsm-off +run-test-zicclsm-off: test-zicclsm-off + $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)zfh=true$(comma)zicclsm=false $(QEMU_OPTS)$<) + # We don't currently support the multiarch system tests undefine MULTIARCH_TESTS diff --git a/tests/tcg/riscv64/test-zicclsm.S b/tests/tcg/riscv64/test-zicclsm.S new file mode 100644 index 0000000000..a2f217c0d1 --- /dev/null +++ b/tests/tcg/riscv64/test-zicclsm.S @@ -0,0 +1,368 @@ +/* + * Test the Zicclsm extension (misaligned load/store support). + * + * This single source is assembled twice: + * - test-zicclsm : run on a CPU with zicclsm=true. Every misaligned + * scalar integer, floating-point and vector + * load/store must complete WITHOUT raising a trap. + * - test-zicclsm-off : built with -DZICCLSM_DISABLED and run on a CPU with + * zicclsm=false. Every misaligned access must raise a + * misaligned load/store exception, with the correct + * mcause and mtval. + * + * Zicclsm governs all regular scalar loads/stores (integer and F/D/Zfh + * floating-point) as well as vector element loads/stores. Floating-point + * loads/stores (flh/flw/fld, fsh/fsw/fsd) are therefore exercised here. + * + * Atomic (A/Zacas/...) accesses are intentionally excluded: they always + * require natural alignment regardless of Zicclsm. Likewise cm.push/cm.pop + * (Zcmp) are excluded, as they are not regular loads/stores. + * + * Register conventions (persist across the whole test; the trap handler only + * clobbers t0-t4): + * s1 = expected mcause for the pending misaligned access + * s2 = expected mtval (the misaligned address) + * s3 = trap counter (incremented by the handler) + * s4 = base address of the aligned data buffer + * s5 = snapshot of s3 taken before an access, used to check the delta + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + .option norelax + .option norvc + +/* RISC-V exception causes (see target/riscv/cpu_bits.h). */ +#define CAUSE_LOAD_MISALIGNED 0x4 +#define CAUSE_STORE_MISALIGNED 0x6 + +/* + * EXPECT sets up the expectation for the access that immediately follows and + * snapshots the trap counter. + * \cause = expected mcause if the access traps + * \off = byte offset from the buffer base; also the expected mtval + */ + .macro EXPECT cause, off + li s1, \cause + addi s2, s4, \off + mv s5, s3 + .endm + +/* + * CHECK validates the outcome of the preceding access. + * - When Zicclsm is disabled, exactly one trap must have fired. + * - When Zicclsm is enabled, no trap must have fired. + */ + .macro CHECK +#ifdef ZICCLSM_DISABLED + addi s5, s5, 1 + bne s3, s5, fail +#else + bne s3, s5, fail +#endif + .endm + +/* In the enabled case, also verify the value returned by scalar loads. */ + .macro CHECK_VALUE value +#ifndef ZICCLSM_DISABLED + li t0, \value + bne a2, t0, fail +#endif + .endm + +/* Verify bytes written by an enabled scalar store. */ + .macro CHECK_BYTE off, value +#ifndef ZICCLSM_DISABLED + lbu t0, \off(s4) + li t1, \value + bne t0, t1, fail +#endif + .endm + + .text + .global _start +_start: + /* Install the trap handler. */ + lla t0, trap + csrw mtvec, t0 + + /* Enable the FP (FS) and Vector (VS) unit state so F/D/V instructions + * do not trap as illegal. 0x6600 = FS[14:13]=11 | VS[10:9]=11. */ + li t0, 0x6600 + csrs mstatus, t0 + + /* Initialise persistent state. */ + li s3, 0 /* trap counter */ + lla s4, buf /* aligned buffer base */ + + /* + * ---- Scalar integer loads ---- + * lh/lhu need 2-byte alignment; lw/lwu 4-byte; ld 8-byte. + */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lh a2, 1(s4) + CHECK + CHECK_VALUE 0x2211 + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lhu a2, 1(s4) + CHECK + CHECK_VALUE 0x2211 + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lw a2, 1(s4) + CHECK + CHECK_VALUE 0x44332211 + EXPECT CAUSE_LOAD_MISALIGNED, 3 + lw a2, 3(s4) + CHECK + CHECK_VALUE 0x66554433 + EXPECT CAUSE_LOAD_MISALIGNED, 1 + lwu a2, 1(s4) + CHECK + CHECK_VALUE 0x44332211 + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + ld a2, 1(s4) + CHECK + CHECK_VALUE 0x8877665544332211 + EXPECT CAUSE_LOAD_MISALIGNED, 3 + ld a2, 3(s4) + CHECK + CHECK_VALUE 0xaa99887766554433 + EXPECT CAUSE_LOAD_MISALIGNED, 7 + ld a2, 7(s4) + CHECK + CHECK_VALUE 0xeeddccbbaa998877 + + /* + * ---- Scalar integer stores ---- + */ + li t6, 0x1122334455667788 + EXPECT CAUSE_STORE_MISALIGNED, 1 + sh t6, 1(s4) + CHECK + CHECK_BYTE 1, 0x88 + CHECK_BYTE 2, 0x77 + + EXPECT CAUSE_STORE_MISALIGNED, 1 + sw t6, 1(s4) + CHECK + CHECK_BYTE 1, 0x88 + CHECK_BYTE 2, 0x77 + CHECK_BYTE 3, 0x66 + CHECK_BYTE 4, 0x55 + EXPECT CAUSE_STORE_MISALIGNED, 3 + sw t6, 3(s4) + CHECK + CHECK_BYTE 3, 0x88 + CHECK_BYTE 4, 0x77 + CHECK_BYTE 5, 0x66 + CHECK_BYTE 6, 0x55 + + EXPECT CAUSE_STORE_MISALIGNED, 1 + sd t6, 1(s4) + CHECK + CHECK_BYTE 1, 0x88 + CHECK_BYTE 2, 0x77 + CHECK_BYTE 3, 0x66 + CHECK_BYTE 4, 0x55 + CHECK_BYTE 5, 0x44 + CHECK_BYTE 6, 0x33 + CHECK_BYTE 7, 0x22 + CHECK_BYTE 8, 0x11 + EXPECT CAUSE_STORE_MISALIGNED, 7 + sd t6, 7(s4) + CHECK + CHECK_BYTE 7, 0x88 + CHECK_BYTE 8, 0x77 + CHECK_BYTE 9, 0x66 + CHECK_BYTE 10, 0x55 + CHECK_BYTE 11, 0x44 + CHECK_BYTE 12, 0x33 + CHECK_BYTE 13, 0x22 + CHECK_BYTE 14, 0x11 + + /* + * ---- Floating-point loads ---- + * flh needs 2-byte alignment; flw 4-byte; fld 8-byte. Their alignment + * is governed by Zicclsm just like the scalar integer forms. + */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + flh fa0, 1(s4) + CHECK + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + flw fa0, 1(s4) + CHECK + EXPECT CAUSE_LOAD_MISALIGNED, 3 + flw fa0, 3(s4) + CHECK + + EXPECT CAUSE_LOAD_MISALIGNED, 1 + fld fa0, 1(s4) + CHECK + EXPECT CAUSE_LOAD_MISALIGNED, 7 + fld fa0, 7(s4) + CHECK + + /* + * ---- Floating-point stores ---- + */ + EXPECT CAUSE_STORE_MISALIGNED, 1 + fsh fa0, 1(s4) + CHECK + + EXPECT CAUSE_STORE_MISALIGNED, 1 + fsw fa0, 1(s4) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 3 + fsw fa0, 3(s4) + CHECK + + EXPECT CAUSE_STORE_MISALIGNED, 1 + fsd fa0, 1(s4) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 7 + fsd fa0, 7(s4) + CHECK + + /* + * ---- Vector unit-stride loads / stores ---- + * A base address that is not aligned to the element size (SEW) is + * misaligned for the first element access. + */ + vsetvli t1, x0, e16, m1, ta, ma + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vle16.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vse16.v v0, (a0) + CHECK + + vsetvli t1, x0, e32, m1, ta, ma + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vle32.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 3 + addi a0, s4, 3 + vse32.v v0, (a0) + CHECK + + vsetvli t1, x0, e64, m1, ta, ma + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vle64.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 7 + addi a0, s4, 7 + vse64.v v0, (a0) + CHECK + + /* + * ---- Vector strided loads / stores ---- + */ + vsetvli t1, x0, e32, m1, ta, ma + li a1, 8 /* stride in bytes */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vlse32.v v0, (a0), a1 + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vsse32.v v0, (a0), a1 + CHECK + + /* ---- Vector indexed loads / stores ---- */ + /* + * Zero indices keep the first element at the deliberately misaligned base. + */ + vmv.v.i v1, 0 + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vluxei32.v v0, (a0), v1 + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vsuxei32.v v0, (a0), v1 + CHECK + + /* ---- Vector segmented loads / stores ---- */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vlseg2e32.v v0, (a0) + CHECK + EXPECT CAUSE_STORE_MISALIGNED, 1 + addi a0, s4, 1 + vsseg2e32.v v0, (a0) + CHECK + + /* + * ---- Vector whole-register load ---- + * Only the whole-register *load* forms carry an element width + * (vl1re32.v => EEW=32), so only they enforce alignment when Zicclsm is + * off. The whole-register store form (vs1r.v) is defined as EEW=8 + * (byte granular) and therefore never faults on misalignment, so it is + * not exercised here. + */ + EXPECT CAUSE_LOAD_MISALIGNED, 1 + addi a0, s4, 1 + vl1re32.v v1, (a0) + CHECK + + /* Success. */ + li a0, 0 + j _exit + + /* + * Trap handler: validate mcause and mtval against the expectation, bump + * the trap counter, then skip past the faulting instruction. The + * instruction length is decoded from its low two bits (0b11 => 4 bytes, + * otherwise a 2-byte compressed instruction). + */ + .balign 4 +trap: + csrr t0, mcause + bne t0, s1, fail + csrr t1, mtval + bne t1, s2, fail + addi s3, s3, 1 + + csrr t0, mepc + lhu t2, 0(t0) + andi t3, t2, 3 + li t4, 3 + bne t3, t4, 1f + addi t0, t0, 4 /* 32-bit instruction */ + j 2f +1: + addi t0, t0, 2 /* 16-bit compressed instruction */ +2: + csrw mepc, t0 + mret + +fail: + li a0, 1 +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 + .balign 64 +buf: + .byte 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 + .byte 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff + .space 240 From 5a9fa8b5820998a5aca02ecf3df12819f7ff2173 Mon Sep 17 00:00:00 2001 From: Zephyr Li Date: Thu, 30 Jul 2026 11:21:20 +0800 Subject: [PATCH 738/860] target/riscv: do not count ECALL in minstret With icount enabled, helper_raise_exception() leaves ECALL in icount_get_raw() because it exits without restoring the current TB state. This makes minstret count an instruction that does not retire. Adjust only the fixed minstret baseline so that mcycle accounting remains unchanged. Add an RV64 softmmu regression test for the issue. Fixes: 4fe8ae09062d ("target/riscv: Combine mhpmcounter and mhpmcounterh") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4087 Signed-off-by: Zephyr Li Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20260730032122.2564190-1-fritchleybohrer@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/op_helper.c | 6 +++ target/riscv/tcg/pmu.c | 44 +++++++++++++----- target/riscv/tcg/pmu.h | 1 + tests/tcg/riscv64/Makefile.softmmu-target | 4 ++ tests/tcg/riscv64/test-minstret-ecall.S | 55 +++++++++++++++++++++++ 5 files changed, 98 insertions(+), 12 deletions(-) create mode 100644 tests/tcg/riscv64/test-minstret-ecall.S diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c index ba3c7da375..acdccd1a97 100644 --- a/target/riscv/tcg/op_helper.c +++ b/target/riscv/tcg/op_helper.c @@ -21,6 +21,9 @@ #include "qemu/osdep.h" #include "cpu.h" #include "target/riscv/tcg/csr.h" +#ifndef CONFIG_USER_ONLY +#include "pmu.h" +#endif #include "internals.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-ldst.h" @@ -47,6 +50,9 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env, void helper_raise_exception(CPURISCVState *env, uint32_t exception) { +#ifndef CONFIG_USER_ONLY + riscv_pmu_decr_instret(env); +#endif riscv_raise_exception(env, exception, 0); } diff --git a/target/riscv/tcg/pmu.c b/target/riscv/tcg/pmu.c index 38ad2737e1..1a4658319b 100644 --- a/target/riscv/tcg/pmu.c +++ b/target/riscv/tcg/pmu.c @@ -49,6 +49,21 @@ static bool riscv_pmu_counter_enabled(RISCVCPU *cpu, uint32_t ctr_idx) } } +static bool riscv_pmu_counter_filtered(CPURISCVState *env, uint64_t cfg) +{ + bool virt_on = env->virt_enabled; + + return (env->priv == PRV_M && (cfg & MHPMEVENT_BIT_MINH)) || + (env->priv == PRV_S && virt_on && + (cfg & MHPMEVENT_BIT_VSINH)) || + (env->priv == PRV_U && virt_on && + (cfg & MHPMEVENT_BIT_VUINH)) || + (env->priv == PRV_S && !virt_on && + (cfg & MHPMEVENT_BIT_SINH)) || + (env->priv == PRV_U && !virt_on && + (cfg & MHPMEVENT_BIT_UINH)); +} + /* * Information needed to update counters: * new_priv, new_virt: To correctly save starting snapshot for the newly @@ -147,12 +162,27 @@ void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, riscv_pmu_icount_update_priv(env, newpriv, new_virt); } +void riscv_pmu_decr_instret(CPURISCVState *env) +{ + if (!icount_enabled() || + (env->mcountinhibit & COUNTEREN_IR) || + riscv_pmu_counter_filtered(env, env->minstretcfg)) { + return; + } + + /* + * minstret is derived from icount, which includes the current + * instruction. Move the baseline forward to exclude an instruction + * that raises an exception and therefore does not retire. + */ + env->pmu_ctrs[2].mhpmcounter_prev++; +} + int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) { uint32_t ctr_idx; CPURISCVState *env = &cpu->env; uint64_t max_val = UINT64_MAX; - bool virt_on = env->virt_enabled; PMUCTRState *counter; gpointer value; @@ -170,17 +200,7 @@ int riscv_pmu_incr_ctr(RISCVCPU *cpu, enum riscv_pmu_event_idx event_idx) return -1; } - /* Privilege mode filtering */ - if ((env->priv == PRV_M && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_MINH)) || - (env->priv == PRV_S && virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VSINH)) || - (env->priv == PRV_U && virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_VUINH)) || - (env->priv == PRV_S && !virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_SINH)) || - (env->priv == PRV_U && !virt_on && - (env->mhpmevent_val[ctr_idx] & MHPMEVENT_BIT_UINH))) { + if (riscv_pmu_counter_filtered(env, env->mhpmevent_val[ctr_idx])) { return 0; } diff --git a/target/riscv/tcg/pmu.h b/target/riscv/tcg/pmu.h index b4f1e469a2..2429c01b77 100644 --- a/target/riscv/tcg/pmu.h +++ b/target/riscv/tcg/pmu.h @@ -36,6 +36,7 @@ int riscv_pmu_setup_timer(CPURISCVState *env, uint64_t value, uint32_t ctr_idx); void riscv_pmu_update_fixed_ctrs(CPURISCVState *env, privilege_mode_t newpriv, bool new_virt); +void riscv_pmu_decr_instret(CPURISCVState *env); RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val, bool upper_half, uint32_t ctr_idx); diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index 908fd008e4..fac0474746 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -24,6 +24,10 @@ EXTRA_RUNS += run-test-mepc-masking run-test-mepc-masking: test-mepc-masking $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) +EXTRA_RUNS += run-test-minstret-ecall +run-test-minstret-ecall: test-minstret-ecall + $(call run-test, $<, $(QEMU) -icount shift=1 $(QEMU_OPTS)$<) + EXTRA_RUNS += run-plugin-doubletrap run-plugin-doubletrap: doubletrap $(call run-test, $<, \ diff --git a/tests/tcg/riscv64/test-minstret-ecall.S b/tests/tcg/riscv64/test-minstret-ecall.S new file mode 100644 index 0000000000..ab268f7f22 --- /dev/null +++ b/tests/tcg/riscv64/test-minstret-ecall.S @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + + .option norvc + + .text + .global _start +_start: + lla t0, trap + csrw mtvec, t0 + + /* + * The first CSR read retires after obtaining s0. The ecall does not + * retire, so the trap handler must observe a difference of one. + */ + csrr s0, minstret + ecall + sub t0, s1, s0 + li t1, 1 + bne t0, t1, fail + + li a0, 0 + j _exit + +trap: + csrr s1, minstret + csrr t0, mcause + li t1, 11 /* Environment call from M-mode */ + bne t0, t1, fail + + csrr t0, mepc + addi t0, t0, 4 + csrw mepc, t0 + mret + +fail: + li a0, 1 + +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + + /* Semihosting call sequence */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 From 51362da8f73b13ad029287ee33e190a212894183 Mon Sep 17 00:00:00 2001 From: Ivan Efremov Date: Sun, 2 Aug 2026 14:31:28 +0300 Subject: [PATCH 739/860] target/riscv: Fix seed CSR sign extension The SEED_OPST_* macros expanded to int expressions. When bit 31 was present, converting a CSR seed value to target_ulong on RV64 sign extended the value and incorrectly set the upper 32 bits. Make the SEED_OPST_* constants unsigned so that CSR values are zero extended on RV64. Fixes: 77442380ecbe ("target/riscv: rvk: add CSR support for Zkr") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4121 Signed-off-by: Ivan Efremov Reviewed-by: Alistair Francis Message-ID: <20260802113130.7818-2-nendensu@ispras.ru> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 3f146a43fe..c01050ce2b 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -1086,11 +1086,11 @@ typedef enum CTRType { (HVICTL_VTI | HVICTL_IID | HVICTL_IPRIOM | HVICTL_IPRIO) /* seed CSR bits */ -#define SEED_OPST (0b11 << 30) -#define SEED_OPST_BIST (0b00 << 30) -#define SEED_OPST_WAIT (0b01 << 30) -#define SEED_OPST_ES16 (0b10 << 30) -#define SEED_OPST_DEAD (0b11 << 30) +#define SEED_OPST (0b11U << 30) +#define SEED_OPST_BIST (0b00U << 30) +#define SEED_OPST_WAIT (0b01U << 30) +#define SEED_OPST_ES16 (0b10U << 30) +#define SEED_OPST_DEAD (0b11U << 30) /* PMU related bits */ #define MIE_LCOFIE (1 << IRQ_PMU_OVF) From e80cf294797870b019d31dd299328aebdfaaa76c Mon Sep 17 00:00:00 2001 From: Ivan Efremov Date: Sun, 2 Aug 2026 14:31:29 +0300 Subject: [PATCH 740/860] tests/qtest: Add seed CSR zero extension test Add a qtest that reads the seed CSR on RV64 machine with Zkr support and verifies that the upper 32 bits are clear. Signed-off-by: Ivan Efremov Reviewed-by: Alistair Francis Message-ID: <20260802113130.7818-3-nendensu@ispras.ru> Signed-off-by: Alistair Francis --- tests/qtest/riscv-csr-test.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c index bb1b0ffed3..7479a5c5bc 100644 --- a/tests/qtest/riscv-csr-test.c +++ b/tests/qtest/riscv-csr-test.c @@ -20,6 +20,12 @@ #define CSR_MVENDORID 0xf11 #define CSR_MISELECT 0x350 +#define CSR_SEED 0x015 + +#define SEED_OPST_MASK (UINT64_C(0x3) << 30) +#define SEED_OPST_ES16 (UINT64_C(0x2) << 30) +#define SEED_OPST_DEAD (UINT64_C(0x3) << 30) + static void run_test_csr(void) { uint64_t res; @@ -46,12 +52,32 @@ static void run_test_csr(void) qtest_quit(qts); } +static void run_test_seed_csr(void) +{ + uint64_t val = 0; + uint64_t opst; + QTestState *qts; + + qts = qtest_init("-machine virt -cpu tt-ascalon"); + + qtest_csr_call(qts, "get_csr", 0, CSR_SEED, &val); + + opst = val & SEED_OPST_MASK; + g_assert_true(opst == SEED_OPST_ES16 || + opst == SEED_OPST_DEAD); + + g_assert_cmphex(val >> 32, ==, 0); + + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); if (qtest_has_machine("virt")) { qtest_add_func("/cpu/csr", run_test_csr); + qtest_add_func("/cpu/csr/seed", run_test_seed_csr); } return g_test_run(); From 6985cfc87512e96c0abfa7fffc836e7bd3889048 Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Fri, 3 Jul 2026 13:03:36 +0800 Subject: [PATCH 741/860] disas/riscv: Fix th.srri decoding target/riscv/xthead.decode defines th.srri as funct6=000100 in bits 31:26, with the 6-bit immediate in bits 25:20. decode_xtheadbb() switches on bits 31:25, i.e. funct6 plus imm[5]. Therefore valid th.srri encodings are 0001000 and 0001001. The current 0000100 and 0000101 cases use the wrong funct6 value and decode valid th.srri instructions as illegal. Fix the cases to match funct6=000100 with both imm[5] values. Fixes: 318df7238b9f ("disas/riscv: Add support for XThead* instructions") Signed-off-by: TANG Tiancheng Reviewed-by: LIU Zhiwei Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20260703-b4-disas-xthead-fix-riscv-next-v4-1-84c566330bc7@linux.alibaba.com> Signed-off-by: Alistair Francis --- disas/riscv-xthead.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c index fcca326d1c..2f2ddb8dd9 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -315,8 +315,10 @@ void decode_xtheadbb(rv_decode *dec, rv_isa isa) op = rv_op_th_revw; } break; - case 0b0000100: - case 0b0000101: op = rv_op_th_srri; break; + case 0b0001000: + case 0b0001001: + op = rv_op_th_srri; + break; } break; case 2: op = rv_op_th_ext; break; From 7a8454ef293085a45686929577b7d5a5daded242 Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Fri, 3 Jul 2026 13:03:37 +0800 Subject: [PATCH 742/860] disas/riscv: Fix 6-bit immediate extraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rv_codec_r2_imm6 is used for XThead instructions whose 6-bit immediate field is encoded in bits 25:20. The old expression left-shifted by 38 and then right-shifted by 60, so it kept only bits 25:22. Use extract32() to decode bits 25:20 directly. This fixes the immediate printed for th.srri and th.tst. Fixes: 318df7238b9f ("disas/riscv: Add support for XThead* instructions") Suggested-by: Alex Bennée Signed-off-by: TANG Tiancheng Reviewed-by: LIU Zhiwei Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20260703-b4-disas-xthead-fix-riscv-next-v4-2-84c566330bc7@linux.alibaba.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index 7f1b262773..fdf75a12ef 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4546,7 +4546,7 @@ static uint32_t operand_zcmp_rlist(rv_inst inst) static uint32_t operand_imm6(rv_inst inst) { - return (inst << 38) >> 60; + return extract32(inst, 20, 6); } static uint32_t operand_imm2(rv_inst inst) From 7899d3c6bdb008dd301b474f3524bad5f37a411e Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Fri, 3 Jul 2026 13:03:38 +0800 Subject: [PATCH 743/860] disas/riscv: Use signed type for vector immediates operand_vimm() sign-extends the 5-bit vector immediate field in bits 19:15, but returns uint32_t. This sends negative immediates through an unsigned type before they are assigned to rv_decode.imm. Return int32_t to match the signed value extracted by the helper. Signed-off-by: TANG Tiancheng Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20260703-b4-disas-xthead-fix-riscv-next-v4-3-84c566330bc7@linux.alibaba.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disas/riscv.c b/disas/riscv.c index fdf75a12ef..967739bdbb 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4487,7 +4487,7 @@ static uint32_t operand_cimmq(rv_inst inst) ((inst << 57) >> 62) << 6; } -static uint32_t operand_vimm(rv_inst inst) +static int32_t operand_vimm(rv_inst inst) { return (int64_t)(inst << 44) >> 59; } From 00cec77a7f9f80eb6dc8f0c27009403729e3641e Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Fri, 3 Jul 2026 13:03:39 +0800 Subject: [PATCH 744/860] disas/riscv: Decode unsigned vector immediates as unsigned rv_codec_v_i decodes all .vi operands with operand_vimm(), which sign-extends bits 19:15. That matches spec operands named imm, but not the .vi forms whose operand is uimm; uimm=31 is decoded as -1 and printed by the shared 6-bit 'u' formatter as 63. Add rv_codec_v_i_u/operand_vuimm() for the 5-bit uimm forms: vsll.vi, vsrl.vi, vsra.vi, vnsrl.wi, vnsra.wi, vssrl.vi, vssra.vi, vnclipu.wi, vnclip.wi, vslideup.vi, vslidedown.vi, vrgather.vi, vaeskf1.vi, vaeskf2.vi, vsm3c.vi, vsm4k.vi and vwsll.vi. The remaining rv_codec_v_i entries are the signed imm forms. Fixes: 07f4964d1785 ("disas/riscv.c: rvv: Add disas support for vector instructions") Fixes: 9d92f56d4a44 ("disas/riscv: Add support for vector crypto extensions") Signed-off-by: TANG Tiancheng Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20260703-b4-disas-xthead-fix-riscv-next-v4-4-84c566330bc7@linux.alibaba.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 45 ++++++++++++++++++++++++++++----------------- disas/riscv.h | 1 + 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 967739bdbb..82b962dc71 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1803,19 +1803,19 @@ const rv_opcode_data rvi_opcode_data[] = { { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsrl.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsra.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, @@ -1909,16 +1909,16 @@ const rv_opcode_data rvi_opcode_data[] = { { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssra.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclipu.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclip.wi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, @@ -2048,15 +2048,15 @@ const rv_opcode_data rvi_opcode_data[] = { { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, 0, 0, 0 }, { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslideup.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslidedown.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, @@ -2144,8 +2144,8 @@ const rv_opcode_data rvi_opcode_data[] = { { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaeskf1.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vaeskf2.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, @@ -2169,14 +2169,14 @@ const rv_opcode_data rvi_opcode_data[] = { { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm3c.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm4k.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, + { "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsll.vi", rv_codec_v_i, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, + { "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, @@ -4492,6 +4492,11 @@ static int32_t operand_vimm(rv_inst inst) return (int64_t)(inst << 44) >> 59; } +static uint32_t operand_vuimm(rv_inst inst) +{ + return extract32(inst, 15, 5); +} + static uint32_t operand_vzimm11(rv_inst inst) { return (inst << 33) >> 53; @@ -4895,6 +4900,12 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa) dec->imm = operand_vimm(inst); dec->vm = operand_vm(inst); break; + case rv_codec_v_i_u: + dec->rd = operand_rd(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_vuimm(inst); + dec->vm = operand_vm(inst); + break; case rv_codec_vror_vi: dec->rd = operand_rd(inst); dec->rs2 = operand_rs2(inst); diff --git a/disas/riscv.h b/disas/riscv.h index 379e642ec8..91ada8b531 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -149,6 +149,7 @@ typedef enum { rv_codec_v_r, rv_codec_v_ldst, rv_codec_v_i, + rv_codec_v_i_u, rv_codec_vsetvli, rv_codec_vsetivli, rv_codec_vror_vi, From dacaa5412b93cc67782decf5e586cc239fce0918 Mon Sep 17 00:00:00 2001 From: TANG Tiancheng Date: Fri, 3 Jul 2026 13:03:40 +0800 Subject: [PATCH 745/860] disas/riscv: Use extract helpers for operand fields Replace shift-based operand extraction with extract32() and sextract32(). For signed immediates, use sextract32() on the field that carries the sign bit and combine it with the remaining extract32() fields. The RISC-V disassembler currently follows target/riscv/internals.h: insn_len() and decodes only 16-bit or 32-bit instruction lengths, so the converted fields are all in the low 32 bits of rv_inst. Suggested-by: Richard Henderson Signed-off-by: TANG Tiancheng Reviewed-by: LIU Zhiwei Reviewed-by: Alistair Francis Message-ID: <20260703-b4-disas-xthead-fix-riscv-next-v4-5-84c566330bc7@linux.alibaba.com> Signed-off-by: Alistair Francis --- disas/riscv.c | 214 +++++++++++++++++++++++++------------------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 82b962dc71..2ba0a6a73a 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4202,82 +4202,82 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) static uint32_t operand_rd(rv_inst inst) { - return (inst << 52) >> 59; + return extract32(inst, 7, 5); } static uint32_t operand_rs1(rv_inst inst) { - return (inst << 44) >> 59; + return extract32(inst, 15, 5); } static uint32_t operand_rs2(rv_inst inst) { - return (inst << 39) >> 59; + return extract32(inst, 20, 5); } static uint32_t operand_rs3(rv_inst inst) { - return (inst << 32) >> 59; + return extract32(inst, 27, 5); } static uint32_t operand_aq(rv_inst inst) { - return (inst << 37) >> 63; + return extract32(inst, 26, 1); } static uint32_t operand_rl(rv_inst inst) { - return (inst << 38) >> 63; + return extract32(inst, 25, 1); } static uint32_t operand_pred(rv_inst inst) { - return (inst << 36) >> 60; + return extract32(inst, 24, 4); } static uint32_t operand_succ(rv_inst inst) { - return (inst << 40) >> 60; + return extract32(inst, 20, 4); } static uint32_t operand_rm(rv_inst inst) { - return (inst << 49) >> 61; + return extract32(inst, 12, 3); } static uint32_t operand_shamt5(rv_inst inst) { - return (inst << 39) >> 59; + return extract32(inst, 20, 5); } static uint32_t operand_shamt6(rv_inst inst) { - return (inst << 38) >> 58; + return extract32(inst, 20, 6); } static uint32_t operand_shamt7(rv_inst inst) { - return (inst << 37) >> 57; + return extract32(inst, 20, 7); } static uint32_t operand_crdq(rv_inst inst) { - return (inst << 59) >> 61; + return extract32(inst, 2, 3); } static uint32_t operand_crs1q(rv_inst inst) { - return (inst << 54) >> 61; + return extract32(inst, 7, 3); } static uint32_t operand_crs1rdq(rv_inst inst) { - return (inst << 54) >> 61; + return extract32(inst, 7, 3); } static uint32_t operand_crs2q(rv_inst inst) { - return (inst << 59) >> 61; + return extract32(inst, 2, 3); } static uint32_t calculate_xreg(uint32_t sreg) @@ -4287,80 +4287,80 @@ static uint32_t calculate_xreg(uint32_t sreg) static uint32_t operand_sreg1(rv_inst inst) { - return calculate_xreg((inst << 54) >> 61); + return calculate_xreg(extract32(inst, 7, 3)); } static uint32_t operand_sreg2(rv_inst inst) { - return calculate_xreg((inst << 59) >> 61); + return calculate_xreg(extract32(inst, 2, 3)); } static uint32_t operand_crd(rv_inst inst) { - return (inst << 52) >> 59; + return extract32(inst, 7, 5); } static uint32_t operand_crs1(rv_inst inst) { - return (inst << 52) >> 59; + return extract32(inst, 7, 5); } static uint32_t operand_crs1rd(rv_inst inst) { - return (inst << 52) >> 59; + return extract32(inst, 7, 5); } static uint32_t operand_crs2(rv_inst inst) { - return (inst << 57) >> 59; + return extract32(inst, 2, 5); } static uint32_t operand_cimmsh5(rv_inst inst) { - return (inst << 57) >> 59; + return extract32(inst, 2, 5); } static uint32_t operand_csr12(rv_inst inst) { - return (inst << 32) >> 52; + return extract32(inst, 20, 12); } static int32_t operand_imm12(rv_inst inst) { - return ((int64_t)inst << 32) >> 52; + return sextract32(inst, 20, 12); } static int32_t operand_imm20(rv_inst inst) { - return (((int64_t)inst << 32) >> 44) << 12; + return sextract32(inst, 12, 20) << 12; } static int32_t operand_jimm20(rv_inst inst) { - return (((int64_t)inst << 32) >> 63) << 20 | - ((inst << 33) >> 54) << 1 | - ((inst << 43) >> 63) << 11 | - ((inst << 44) >> 56) << 12; + return sextract32(inst, 31, 1) << 20 | + extract32(inst, 21, 10) << 1 | + extract32(inst, 20, 1) << 11 | + extract32(inst, 12, 8) << 12; } static int32_t operand_simm12(rv_inst inst) { - return (((int64_t)inst << 32) >> 57) << 5 | - (inst << 52) >> 59; + return sextract32(inst, 25, 7) << 5 | + extract32(inst, 7, 5); } static int32_t operand_sbimm12(rv_inst inst) { - return (((int64_t)inst << 32) >> 63) << 12 | - ((inst << 33) >> 58) << 5 | - ((inst << 52) >> 60) << 1 | - ((inst << 56) >> 63) << 11; + return sextract32(inst, 31, 1) << 12 | + extract32(inst, 25, 6) << 5 | + extract32(inst, 8, 4) << 1 | + extract32(inst, 7, 1) << 11; } static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) { - int imm = ((inst << 51) >> 63) << 5 | - (inst << 57) >> 59; + int imm = extract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); if (isa == rv128) { imm = imm ? imm : 64; } @@ -4369,8 +4369,8 @@ static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) { - int imm = ((inst << 51) >> 63) << 5 | - (inst << 57) >> 59; + int imm = extract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); if (isa == rv128) { imm = imm | (imm & 32) << 1; imm = imm ? imm : 64; @@ -4380,116 +4380,116 @@ static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) static int32_t operand_cimmi(rv_inst inst) { - return (((int64_t)inst << 51) >> 63) << 5 | - (inst << 57) >> 59; + return sextract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); } static int32_t operand_cimmui(rv_inst inst) { - return (((int64_t)inst << 51) >> 63) << 17 | - ((inst << 57) >> 59) << 12; + return sextract32(inst, 12, 1) << 17 | + extract32(inst, 2, 5) << 12; } static uint32_t operand_cimmlwsp(rv_inst inst) { - return ((inst << 51) >> 63) << 5 | - ((inst << 57) >> 61) << 2 | - ((inst << 60) >> 62) << 6; + return extract32(inst, 12, 1) << 5 | + extract32(inst, 4, 3) << 2 | + extract32(inst, 2, 2) << 6; } static uint32_t operand_cimmldsp(rv_inst inst) { - return ((inst << 51) >> 63) << 5 | - ((inst << 57) >> 62) << 3 | - ((inst << 59) >> 61) << 6; + return extract32(inst, 12, 1) << 5 | + extract32(inst, 5, 2) << 3 | + extract32(inst, 2, 3) << 6; } static uint32_t operand_cimmlqsp(rv_inst inst) { - return ((inst << 51) >> 63) << 5 | - ((inst << 57) >> 63) << 4 | - ((inst << 58) >> 60) << 6; + return extract32(inst, 12, 1) << 5 | + extract32(inst, 6, 1) << 4 | + extract32(inst, 2, 4) << 6; } static int32_t operand_cimm16sp(rv_inst inst) { - return (((int64_t)inst << 51) >> 63) << 9 | - ((inst << 57) >> 63) << 4 | - ((inst << 58) >> 63) << 6 | - ((inst << 59) >> 62) << 7 | - ((inst << 61) >> 63) << 5; + return sextract32(inst, 12, 1) << 9 | + extract32(inst, 6, 1) << 4 | + extract32(inst, 5, 1) << 6 | + extract32(inst, 3, 2) << 7 | + extract32(inst, 2, 1) << 5; } static int32_t operand_cimmj(rv_inst inst) { - return (((int64_t)inst << 51) >> 63) << 11 | - ((inst << 52) >> 63) << 4 | - ((inst << 53) >> 62) << 8 | - ((inst << 55) >> 63) << 10 | - ((inst << 56) >> 63) << 6 | - ((inst << 57) >> 63) << 7 | - ((inst << 58) >> 61) << 1 | - ((inst << 61) >> 63) << 5; + return sextract32(inst, 12, 1) << 11 | + extract32(inst, 11, 1) << 4 | + extract32(inst, 9, 2) << 8 | + extract32(inst, 8, 1) << 10 | + extract32(inst, 7, 1) << 6 | + extract32(inst, 6, 1) << 7 | + extract32(inst, 3, 3) << 1 | + extract32(inst, 2, 1) << 5; } static int32_t operand_cimmb(rv_inst inst) { - return (((int64_t)inst << 51) >> 63) << 8 | - ((inst << 52) >> 62) << 3 | - ((inst << 57) >> 62) << 6 | - ((inst << 59) >> 62) << 1 | - ((inst << 61) >> 63) << 5; + return sextract32(inst, 12, 1) << 8 | + extract32(inst, 10, 2) << 3 | + extract32(inst, 5, 2) << 6 | + extract32(inst, 3, 2) << 1 | + extract32(inst, 2, 1) << 5; } static uint32_t operand_cimmswsp(rv_inst inst) { - return ((inst << 51) >> 60) << 2 | - ((inst << 55) >> 62) << 6; + return extract32(inst, 9, 4) << 2 | + extract32(inst, 7, 2) << 6; } static uint32_t operand_cimmsdsp(rv_inst inst) { - return ((inst << 51) >> 61) << 3 | - ((inst << 54) >> 61) << 6; + return extract32(inst, 10, 3) << 3 | + extract32(inst, 7, 3) << 6; } static uint32_t operand_cimmsqsp(rv_inst inst) { - return ((inst << 51) >> 62) << 4 | - ((inst << 53) >> 60) << 6; + return extract32(inst, 11, 2) << 4 | + extract32(inst, 7, 4) << 6; } static uint32_t operand_cimm4spn(rv_inst inst) { - return ((inst << 51) >> 62) << 4 | - ((inst << 53) >> 60) << 6 | - ((inst << 57) >> 63) << 2 | - ((inst << 58) >> 63) << 3; + return extract32(inst, 11, 2) << 4 | + extract32(inst, 7, 4) << 6 | + extract32(inst, 6, 1) << 2 | + extract32(inst, 5, 1) << 3; } static uint32_t operand_cimmw(rv_inst inst) { - return ((inst << 51) >> 61) << 3 | - ((inst << 57) >> 63) << 2 | - ((inst << 58) >> 63) << 6; + return extract32(inst, 10, 3) << 3 | + extract32(inst, 6, 1) << 2 | + extract32(inst, 5, 1) << 6; } static uint32_t operand_cimmd(rv_inst inst) { - return ((inst << 51) >> 61) << 3 | - ((inst << 57) >> 62) << 6; + return extract32(inst, 10, 3) << 3 | + extract32(inst, 5, 2) << 6; } static uint32_t operand_cimmq(rv_inst inst) { - return ((inst << 51) >> 62) << 4 | - ((inst << 53) >> 63) << 8 | - ((inst << 57) >> 62) << 6; + return extract32(inst, 11, 2) << 4 | + extract32(inst, 10, 1) << 8 | + extract32(inst, 5, 2) << 6; } static int32_t operand_vimm(rv_inst inst) { - return (int64_t)(inst << 44) >> 59; + return sextract32(inst, 15, 5); } static uint32_t operand_vuimm(rv_inst inst) @@ -4499,54 +4499,54 @@ static uint32_t operand_vuimm(rv_inst inst) static uint32_t operand_vzimm11(rv_inst inst) { - return (inst << 33) >> 53; + return extract32(inst, 20, 11); } static uint32_t operand_vzimm10(rv_inst inst) { - return (inst << 34) >> 54; + return extract32(inst, 20, 10); } static uint32_t operand_vzimm6(rv_inst inst) { - return ((inst << 37) >> 63) << 5 | - ((inst << 44) >> 59); + return extract32(inst, 26, 1) << 5 | + extract32(inst, 15, 5); } static uint32_t operand_bs(rv_inst inst) { - return (inst << 32) >> 62; + return extract32(inst, 30, 2); } static uint32_t operand_rnum(rv_inst inst) { - return (inst << 40) >> 60; + return extract32(inst, 20, 4); } static uint32_t operand_vm(rv_inst inst) { - return (inst << 38) >> 63; + return extract32(inst, 25, 1); } static uint32_t operand_uimm_c_lb(rv_inst inst) { - return (((inst << 58) >> 63) << 1) | - ((inst << 57) >> 63); + return extract32(inst, 5, 1) << 1 | + extract32(inst, 6, 1); } static uint32_t operand_uimm_c_lh(rv_inst inst) { - return (((inst << 58) >> 63) << 1); + return extract32(inst, 5, 1) << 1; } static uint32_t operand_zcmp_spimm(rv_inst inst) { - return ((inst << 60) >> 62) << 4; + return extract32(inst, 2, 2) << 4; } static uint32_t operand_zcmp_rlist(rv_inst inst) { - return ((inst << 56) >> 60); + return extract32(inst, 4, 4); } static uint32_t operand_imm6(rv_inst inst) @@ -4556,17 +4556,17 @@ static uint32_t operand_imm6(rv_inst inst) static uint32_t operand_imm2(rv_inst inst) { - return (inst << 37) >> 62; + return extract32(inst, 25, 2); } static uint32_t operand_immh(rv_inst inst) { - return (inst << 32) >> 58; + return extract32(inst, 26, 6); } static uint32_t operand_imml(rv_inst inst) { - return (inst << 38) >> 58; + return extract32(inst, 20, 6); } static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm) @@ -4585,12 +4585,12 @@ static uint32_t operand_zcmp_stack_adj(rv_inst inst, rv_isa isa) static uint32_t operand_tbl_index(rv_inst inst) { - return ((inst << 54) >> 56); + return extract32(inst, 2, 8); } static uint32_t operand_lpl(rv_inst inst) { - return inst >> 12; + return extract32(inst, 12, 20); } /* decode operands */ From f16fd27b3fe3c305d95c61d3516e458c45c6e8c9 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 17 Jul 2026 13:23:40 +0200 Subject: [PATCH 746/860] hw/riscv/riscv-iommu: fix U-bit check to apply only to leaf S/VS-stage PTEs Commit b795ea0ba471 ("hw/riscv/riscv-iommu.c: fault when !PTE_U and no priv access") placed its check ahead of the leaf-vs-non-leaf branch in riscv_iommu_spa_fetch(), so it fires on every PTE walked, including non-leaf/table entries. Per the RISC-V privileged spec's address translation algorithm (Sv39/Sv48/etc., the "leaf PTE has been reached" step, followed separately by the U-bit permission check), the U bit is only defined and checked for the leaf PTE reached at the end of the walk -- non-leaf PTEs don't carry a meaningful U bit at all. Move the check after the leaf/non-leaf branch, alongside the other leaf-only checks, mirroring how the G_STAGE U-bit check (added in 9158c900ab30) is already correctly placed. Fixes: b795ea0ba471 ("hw/riscv/riscv-iommu.c: fault when !PTE_U and no priv access") Signed-off-by: Andrew Jones Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20260717112340.1071148-1-andrew.jones@oss.qualcomm.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index f6865d1e16..ed9fb09f8b 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -476,13 +476,6 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, break; /* Invalid PTE */ } else if (pte & PTE_RESERVED(false)) { break; /* Reserved PTE bits set */ - } else if (!(pte & PTE_U) && !pv) { - /* - * All accesses are assumed to be User mode unless - * process_id is valid (pv). In case we have a - * non-user mode PTE and !pv we need to fault. - */ - break; } 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) { @@ -491,6 +484,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, break; /* Reserved leaf PTE flags: PTE_W + PTE_X */ } else if (ppn & ((1ULL << (va_skip - TARGET_PAGE_BITS)) - 1)) { break; /* Misaligned PPN */ + } else if (!(pte & PTE_U) && !pv) { + /* + * All accesses are assumed to be User mode unless + * process_id is valid (pv). In case we have a + * non-user mode leaf PTE and !pv we need to fault. + */ + break; } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { break; /* Read access check failed */ } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { From aa51eec004461f671f878145d3fc921c6cabee48 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Fri, 17 Jul 2026 16:45:25 +0200 Subject: [PATCH 747/860] hw/riscv/riscv-iommu: preserve requested perm in spa_fetch() b18e3f0e2d0f fixed spa_fetch() faults whose TTYP used the leaf PTE permission instead of the original request permission. However, it kept that request-narrowed value in iotlb->perm after a successful walk, and riscv_iommu_translate() caches iotlb->perm for later accesses to the same IOVA. That means a write to an RW mapping can cache the entry as write-only. A later read then hits the cache and faults even though the mapping allows it, which showed up in NVMe testing as bogus completions and controller timeouts. Keep the requested permission in a separate req_perm and use it for all permission checks and fault-type decisions. Accumulate the leaf permissions separately and copy them to iotlb->perm only after the full walk succeeds, so cached entries describe the mapping rather than the current request. Since faults leave iotlb->perm as the original request, the S-stage and G-stage TTYP fixes remain intact. Fixes: b18e3f0e2d0f ("hw/riscv/riscv-iommu.c: fix fault type for spa_fetch() faults") Signed-off-by: Andrew Jones Reviewed-by: Daniel Henrique Barboza Message-ID: <20260717144525.1154204-1-andrew.jones@oss.qualcomm.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 50 ++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index ed9fb09f8b..323a041b4a 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -281,7 +281,7 @@ static hwaddr riscv_iommu_napot_page_mask(hwaddr ppn, hwaddr addr, hwaddr *out) static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, IOMMUTLBEntry *iotlb) { - IOMMUAccessFlags pte_perm; + IOMMUAccessFlags trans_perm = IOMMU_NONE; dma_addr_t addr, base; uint64_t satp, gatp, pte; bool en_s, en_g; @@ -298,6 +298,14 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } pass; MemTxResult ret; bool pv = !!ctx->process_id; + /* + * Keep the request permission separate from iotlb->perm. G-stage + * walks translate S-stage PTE addresses before the real leaf is + * reached, but permission checks and fault types must still use the + * original request. A successful walk leaves iotlb->perm with the + * effective leaf permission for the translation cache. + */ + const IOMMUAccessFlags req_perm = iotlb->perm; satp = get_field(ctx->satp, RISCV_IOMMU_ATP_MODE_FIELD); gatp = get_field(ctx->gatp, RISCV_IOMMU_ATP_MODE_FIELD); @@ -316,7 +324,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, * means we can't do an early MSI check unless we have * strictly !en_s. */ - if (!en_s && (iotlb->perm & IOMMU_WO) && + if (!en_s && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, iotlb->iova)) { iotlb->target_as = &s->trap_as; iotlb->translated_addr = iotlb->iova; @@ -434,13 +442,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, masked_msbs = (addr >> (va_len - 1)) & mask; if (masked_msbs != 0 && masked_msbs != mask) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_S; } } else { if ((addr & va_mask) != addr) { - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS; } @@ -465,8 +473,8 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, MEMTXATTRS_UNSPECIFIED); } if (ret != MEMTX_OK) { - return (iotlb->perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT - : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; + return (req_perm & IOMMU_WO) ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT + : RISCV_IOMMU_FQ_CAUSE_RD_FAULT; } sc[pass].step++; @@ -491,13 +499,13 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, * non-user mode leaf PTE and !pv we need to fault. */ break; - } else if ((iotlb->perm & IOMMU_RO) && !(pte & PTE_R)) { + } else if ((req_perm & IOMMU_RO) && !(pte & PTE_R)) { break; /* Read access check failed */ - } else if ((iotlb->perm & IOMMU_WO) && !(pte & PTE_W)) { + } else if ((req_perm & IOMMU_WO) && !(pte & PTE_W)) { break; /* Write access check failed */ } else if (!ade && !(pte & PTE_A)) { break; /* Access bit not set */ - } else if ((iotlb->perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { + } else if ((req_perm & IOMMU_WO) && !ade && !(pte & PTE_D)) { break; /* Dirty bit not set */ } else if (pass == G_STAGE && !(pte & PTE_U)) { /* @@ -532,21 +540,20 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, addr = iotlb->iova; continue; } + + /* Cache the effective permission, not this request's subset. */ + IOMMUAccessFlags leaf_perm = (pte & PTE_W) ? + ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) : + IOMMU_RO; + + trans_perm = trans_perm == IOMMU_NONE ? + leaf_perm : trans_perm & leaf_perm; + /* Translation phase completed (GPA or SPA) */ iotlb->translated_addr = base; - /* - * Do a bit_and between the PTE bits and the original - * request flags to determine the exact permission we - * need, i.e. if the original request is RO and the - * PTE has RW flags the actual perm is RO. - */ - pte_perm = (pte & PTE_W) ? ((pte & PTE_R) ? IOMMU_RW : IOMMU_WO) - : IOMMU_RO; - iotlb->perm &= pte_perm; - /* Check MSI GPA address match */ - if (pass == S_STAGE && (iotlb->perm & IOMMU_WO) && + if (pass == S_STAGE && (req_perm & IOMMU_WO) && riscv_iommu_msi_check(s, ctx, base)) { /* Trap MSI writes and return GPA address. */ iotlb->target_as = &s->trap_as; @@ -563,6 +570,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, continue; } + iotlb->perm = trans_perm; return 0; } @@ -587,7 +595,7 @@ static int riscv_iommu_spa_fetch(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, */ iotlb->translated_addr = addr; - return (iotlb->perm & IOMMU_WO) ? + return (req_perm & IOMMU_WO) ? (pass ? RISCV_IOMMU_FQ_CAUSE_WR_FAULT_VS : RISCV_IOMMU_FQ_CAUSE_WR_FAULT_S) : (pass ? RISCV_IOMMU_FQ_CAUSE_RD_FAULT_VS : From bd1ee9ab3c39f269d8bea8acf83ea833a69a6e10 Mon Sep 17 00:00:00 2001 From: A-Shehab Date: Sun, 26 Jul 2026 11:05:37 +0300 Subject: [PATCH 748/860] target/riscv: allow menvcfg/henvcfg LPE and SSE bits on RV32 The Zicfilp landing-pad enable (LPE, bit 2) and Zicfiss shadow-stack enable (SSE, bit 3) controls live in the low 32 bits of menvcfg and henvcfg, and the CFI specification defines them for both RV32 and RV64. QEMU only adds MENVCFG_LPE/MENVCFG_SSE (and the henvcfg equivalents) to the writable mask inside the "riscv_cpu_mxl(env) == MXL_RV64" block, so on RV32 these bits are silently dropped and the features cannot be enabled. This is inconsistent with write_senvcfg(), which already handles SENVCFG_LPE/SENVCFG_SSE regardless of MXLEN. Hoist the LPE/SSE mask handling out of the RV64-only block in write_menvcfg() and write_henvcfg() so the bits become writable on RV32 as well. The upper-half writers (write_menvcfgh/write_henvcfgh) are unaffected because these bits reside in the low 32 bits. Reproducible on qemu-system-riscv32 -cpu rv32,zicfilp=true,zicfiss=true: an M-mode write of menvcfg.{LPE,SSE} reads back as zero, while the same program on rv64 keeps the bits set. Fixes: 4923f672e3d7 ("target/riscv: Introduce elp state and enabling controls for zicfilp") Fixes: 8205bc127a83 ("target/riscv: introduce ssp and enabling controls for zicfiss") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4045 Signed-off-by: A-Shehab Reviewed-by: Daniel Henrique Barboza Message-ID: <20260726080537.13913-1-ahshehab24@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 45 +++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index a90449b87f..ffd751caa5 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -3221,6 +3221,19 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, MENVCFG_CBZE; bool stce_changed = false; + /* + * menvcfg.LPE (Zicfilp) and menvcfg.SSE (Zicfiss) reside in the low + * 32 bits and are defined for both RV32 and RV64, so they must be + * writable regardless of MXLEN. + */ + if (cfg->ext_zicfilp) { + mask |= MENVCFG_LPE; + } + + if (cfg->ext_zicfiss) { + mask |= MENVCFG_SSE; + } + if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | (cfg->ext_sstc ? MENVCFG_STCE : 0) | @@ -3228,14 +3241,6 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, (cfg->ext_svadu ? MENVCFG_ADUE : 0) | (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); - if (env_archcpu(env)->cfg.ext_zicfilp) { - mask |= MENVCFG_LPE; - } - - if (env_archcpu(env)->cfg.ext_zicfiss) { - mask |= MENVCFG_SSE; - } - /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (env_archcpu(env)->cfg.ext_smnpm && get_field(val, MENVCFG_PMM) != PMM_FIELD_RESERVED) { @@ -3383,20 +3388,24 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, return ret; } + /* + * henvcfg.LPE (Zicfilp) and henvcfg.SSE (Zicfiss) reside in the low + * 32 bits and are defined for both RV32 and RV64, so they must be + * writable regardless of MXLEN. + */ + if (cfg->ext_zicfilp) { + mask |= HENVCFG_LPE; + } + + /* H can light up SSE for VS only if HS had it from menvcfg */ + if (cfg->ext_zicfiss && get_field(env->menvcfg, MENVCFG_SSE)) { + mask |= HENVCFG_SSE; + } + if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | HENVCFG_DTE); - if (env_archcpu(env)->cfg.ext_zicfilp) { - mask |= HENVCFG_LPE; - } - - /* H can light up SSE for VS only if HS had it from menvcfg */ - if (env_archcpu(env)->cfg.ext_zicfiss && - get_field(env->menvcfg, MENVCFG_SSE)) { - mask |= HENVCFG_SSE; - } - /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (env_archcpu(env)->cfg.ext_ssnpm && get_field(val, HENVCFG_PMM) != PMM_FIELD_RESERVED) { From df975586d89f59c93c944815c69bef188bbde572 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:51 -0700 Subject: [PATCH 749/860] disas/riscv: Move operand extractors earlier in file Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-2-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 790 +++++++++++++++++++++++++------------------------- 1 file changed, 395 insertions(+), 395 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 2ba0a6a73a..e3fbc26626 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1259,6 +1259,401 @@ static const rv_comp_data rvcp_fsgnjx_q[] = { { rv_op_illegal, NULL } }; +/* operand extractors */ + +static uint32_t operand_rd(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_rs1(rv_inst inst) +{ + return extract32(inst, 15, 5); +} + +static uint32_t operand_rs2(rv_inst inst) +{ + return extract32(inst, 20, 5); +} + +static uint32_t operand_rs3(rv_inst inst) +{ + return extract32(inst, 27, 5); +} + +static uint32_t operand_aq(rv_inst inst) +{ + return extract32(inst, 26, 1); +} + +static uint32_t operand_rl(rv_inst inst) +{ + return extract32(inst, 25, 1); +} + +static uint32_t operand_pred(rv_inst inst) +{ + return extract32(inst, 24, 4); +} + +static uint32_t operand_succ(rv_inst inst) +{ + return extract32(inst, 20, 4); +} + +static uint32_t operand_rm(rv_inst inst) +{ + return extract32(inst, 12, 3); +} + +static uint32_t operand_shamt5(rv_inst inst) +{ + return extract32(inst, 20, 5); +} + +static uint32_t operand_shamt6(rv_inst inst) +{ + return extract32(inst, 20, 6); +} + +static uint32_t operand_shamt7(rv_inst inst) +{ + return extract32(inst, 20, 7); +} + +static uint32_t operand_crdq(rv_inst inst) +{ + return extract32(inst, 2, 3); +} + +static uint32_t operand_crs1q(rv_inst inst) +{ + return extract32(inst, 7, 3); +} + +static uint32_t operand_crs1rdq(rv_inst inst) +{ + return extract32(inst, 7, 3); +} + +static uint32_t operand_crs2q(rv_inst inst) +{ + return extract32(inst, 2, 3); +} + +static uint32_t calculate_xreg(uint32_t sreg) +{ + return sreg < 2 ? sreg + 8 : sreg + 16; +} + +static uint32_t operand_sreg1(rv_inst inst) +{ + return calculate_xreg(extract32(inst, 7, 3)); +} + +static uint32_t operand_sreg2(rv_inst inst) +{ + return calculate_xreg(extract32(inst, 2, 3)); +} + +static uint32_t operand_crd(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_crs1(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_crs1rd(rv_inst inst) +{ + return extract32(inst, 7, 5); +} + +static uint32_t operand_crs2(rv_inst inst) +{ + return extract32(inst, 2, 5); +} + +static uint32_t operand_cimmsh5(rv_inst inst) +{ + return extract32(inst, 2, 5); +} + +static uint32_t operand_csr12(rv_inst inst) +{ + return extract32(inst, 20, 12); +} + +static int32_t operand_imm12(rv_inst inst) +{ + return sextract32(inst, 20, 12); +} + +static int32_t operand_imm20(rv_inst inst) +{ + return sextract32(inst, 12, 20) << 12; +} + +static int32_t operand_jimm20(rv_inst inst) +{ + return sextract32(inst, 31, 1) << 20 | + extract32(inst, 21, 10) << 1 | + extract32(inst, 20, 1) << 11 | + extract32(inst, 12, 8) << 12; +} + +static int32_t operand_simm12(rv_inst inst) +{ + return sextract32(inst, 25, 7) << 5 | + extract32(inst, 7, 5); +} + +static int32_t operand_sbimm12(rv_inst inst) +{ + return sextract32(inst, 31, 1) << 12 | + extract32(inst, 25, 6) << 5 | + extract32(inst, 8, 4) << 1 | + extract32(inst, 7, 1) << 11; +} + +static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) +{ + int imm = extract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); + if (isa == rv128) { + imm = imm ? imm : 64; + } + return imm; +} + +static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) +{ + int imm = extract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); + if (isa == rv128) { + imm = imm | (imm & 32) << 1; + imm = imm ? imm : 64; + } + return imm; +} + +static int32_t operand_cimmi(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 5 | + extract32(inst, 2, 5); +} + +static int32_t operand_cimmui(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 17 | + extract32(inst, 2, 5) << 12; +} + +static uint32_t operand_cimmlwsp(rv_inst inst) +{ + return extract32(inst, 12, 1) << 5 | + extract32(inst, 4, 3) << 2 | + extract32(inst, 2, 2) << 6; +} + +static uint32_t operand_cimmldsp(rv_inst inst) +{ + return extract32(inst, 12, 1) << 5 | + extract32(inst, 5, 2) << 3 | + extract32(inst, 2, 3) << 6; +} + +static uint32_t operand_cimmlqsp(rv_inst inst) +{ + return extract32(inst, 12, 1) << 5 | + extract32(inst, 6, 1) << 4 | + extract32(inst, 2, 4) << 6; +} + +static int32_t operand_cimm16sp(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 9 | + extract32(inst, 6, 1) << 4 | + extract32(inst, 5, 1) << 6 | + extract32(inst, 3, 2) << 7 | + extract32(inst, 2, 1) << 5; +} + +static int32_t operand_cimmj(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 11 | + extract32(inst, 11, 1) << 4 | + extract32(inst, 9, 2) << 8 | + extract32(inst, 8, 1) << 10 | + extract32(inst, 7, 1) << 6 | + extract32(inst, 6, 1) << 7 | + extract32(inst, 3, 3) << 1 | + extract32(inst, 2, 1) << 5; +} + +static int32_t operand_cimmb(rv_inst inst) +{ + return sextract32(inst, 12, 1) << 8 | + extract32(inst, 10, 2) << 3 | + extract32(inst, 5, 2) << 6 | + extract32(inst, 3, 2) << 1 | + extract32(inst, 2, 1) << 5; +} + +static uint32_t operand_cimmswsp(rv_inst inst) +{ + return extract32(inst, 9, 4) << 2 | + extract32(inst, 7, 2) << 6; +} + +static uint32_t operand_cimmsdsp(rv_inst inst) +{ + return extract32(inst, 10, 3) << 3 | + extract32(inst, 7, 3) << 6; +} + +static uint32_t operand_cimmsqsp(rv_inst inst) +{ + return extract32(inst, 11, 2) << 4 | + extract32(inst, 7, 4) << 6; +} + +static uint32_t operand_cimm4spn(rv_inst inst) +{ + return extract32(inst, 11, 2) << 4 | + extract32(inst, 7, 4) << 6 | + extract32(inst, 6, 1) << 2 | + extract32(inst, 5, 1) << 3; +} + +static uint32_t operand_cimmw(rv_inst inst) +{ + return extract32(inst, 10, 3) << 3 | + extract32(inst, 6, 1) << 2 | + extract32(inst, 5, 1) << 6; +} + +static uint32_t operand_cimmd(rv_inst inst) +{ + return extract32(inst, 10, 3) << 3 | + extract32(inst, 5, 2) << 6; +} + +static uint32_t operand_cimmq(rv_inst inst) +{ + return extract32(inst, 11, 2) << 4 | + extract32(inst, 10, 1) << 8 | + extract32(inst, 5, 2) << 6; +} + +static int32_t operand_vimm(rv_inst inst) +{ + return sextract32(inst, 15, 5); +} + +static uint32_t operand_vuimm(rv_inst inst) +{ + return extract32(inst, 15, 5); +} + +static uint32_t operand_vzimm11(rv_inst inst) +{ + return extract32(inst, 20, 11); +} + +static uint32_t operand_vzimm10(rv_inst inst) +{ + return extract32(inst, 20, 10); +} + +static uint32_t operand_vzimm6(rv_inst inst) +{ + return extract32(inst, 26, 1) << 5 | + extract32(inst, 15, 5); +} + +static uint32_t operand_bs(rv_inst inst) +{ + return extract32(inst, 30, 2); +} + +static uint32_t operand_rnum(rv_inst inst) +{ + return extract32(inst, 20, 4); +} + +static uint32_t operand_vm(rv_inst inst) +{ + return extract32(inst, 25, 1); +} + +static uint32_t operand_uimm_c_lb(rv_inst inst) +{ + return extract32(inst, 5, 1) << 1 | + extract32(inst, 6, 1); +} + +static uint32_t operand_uimm_c_lh(rv_inst inst) +{ + return extract32(inst, 5, 1) << 1; +} + +static uint32_t operand_zcmp_spimm(rv_inst inst) +{ + return extract32(inst, 2, 2) << 4; +} + +static uint32_t operand_zcmp_rlist(rv_inst inst) +{ + return extract32(inst, 4, 4); +} + +static uint32_t operand_imm6(rv_inst inst) +{ + return extract32(inst, 20, 6); +} + +static uint32_t operand_imm2(rv_inst inst) +{ + return extract32(inst, 25, 2); +} + +static uint32_t operand_immh(rv_inst inst) +{ + return extract32(inst, 26, 6); +} + +static uint32_t operand_imml(rv_inst inst) +{ + return extract32(inst, 20, 6); +} + +static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm) +{ + int xlen_bytes_log2 = isa == rv64 ? 3 : 2; + int regs = rlist == 15 ? 13 : rlist - 3; + uint32_t stack_adj_base = ROUND_UP(regs << xlen_bytes_log2, 16); + return stack_adj_base + spimm; +} + +static uint32_t operand_zcmp_stack_adj(rv_inst inst, rv_isa isa) +{ + return calculate_stack_adj(isa, operand_zcmp_rlist(inst), + operand_zcmp_spimm(inst)); +} + +static uint32_t operand_tbl_index(rv_inst inst) +{ + return extract32(inst, 2, 8); +} + +static uint32_t operand_lpl(rv_inst inst) +{ + return extract32(inst, 12, 20); +} + /* instruction metadata */ const rv_opcode_data rvi_opcode_data[] = { @@ -4198,401 +4593,6 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) dec->op = op; } -/* operand extractors */ - -static uint32_t operand_rd(rv_inst inst) -{ - return extract32(inst, 7, 5); -} - -static uint32_t operand_rs1(rv_inst inst) -{ - return extract32(inst, 15, 5); -} - -static uint32_t operand_rs2(rv_inst inst) -{ - return extract32(inst, 20, 5); -} - -static uint32_t operand_rs3(rv_inst inst) -{ - return extract32(inst, 27, 5); -} - -static uint32_t operand_aq(rv_inst inst) -{ - return extract32(inst, 26, 1); -} - -static uint32_t operand_rl(rv_inst inst) -{ - return extract32(inst, 25, 1); -} - -static uint32_t operand_pred(rv_inst inst) -{ - return extract32(inst, 24, 4); -} - -static uint32_t operand_succ(rv_inst inst) -{ - return extract32(inst, 20, 4); -} - -static uint32_t operand_rm(rv_inst inst) -{ - return extract32(inst, 12, 3); -} - -static uint32_t operand_shamt5(rv_inst inst) -{ - return extract32(inst, 20, 5); -} - -static uint32_t operand_shamt6(rv_inst inst) -{ - return extract32(inst, 20, 6); -} - -static uint32_t operand_shamt7(rv_inst inst) -{ - return extract32(inst, 20, 7); -} - -static uint32_t operand_crdq(rv_inst inst) -{ - return extract32(inst, 2, 3); -} - -static uint32_t operand_crs1q(rv_inst inst) -{ - return extract32(inst, 7, 3); -} - -static uint32_t operand_crs1rdq(rv_inst inst) -{ - return extract32(inst, 7, 3); -} - -static uint32_t operand_crs2q(rv_inst inst) -{ - return extract32(inst, 2, 3); -} - -static uint32_t calculate_xreg(uint32_t sreg) -{ - return sreg < 2 ? sreg + 8 : sreg + 16; -} - -static uint32_t operand_sreg1(rv_inst inst) -{ - return calculate_xreg(extract32(inst, 7, 3)); -} - -static uint32_t operand_sreg2(rv_inst inst) -{ - return calculate_xreg(extract32(inst, 2, 3)); -} - -static uint32_t operand_crd(rv_inst inst) -{ - return extract32(inst, 7, 5); -} - -static uint32_t operand_crs1(rv_inst inst) -{ - return extract32(inst, 7, 5); -} - -static uint32_t operand_crs1rd(rv_inst inst) -{ - return extract32(inst, 7, 5); -} - -static uint32_t operand_crs2(rv_inst inst) -{ - return extract32(inst, 2, 5); -} - -static uint32_t operand_cimmsh5(rv_inst inst) -{ - return extract32(inst, 2, 5); -} - -static uint32_t operand_csr12(rv_inst inst) -{ - return extract32(inst, 20, 12); -} - -static int32_t operand_imm12(rv_inst inst) -{ - return sextract32(inst, 20, 12); -} - -static int32_t operand_imm20(rv_inst inst) -{ - return sextract32(inst, 12, 20) << 12; -} - -static int32_t operand_jimm20(rv_inst inst) -{ - return sextract32(inst, 31, 1) << 20 | - extract32(inst, 21, 10) << 1 | - extract32(inst, 20, 1) << 11 | - extract32(inst, 12, 8) << 12; -} - -static int32_t operand_simm12(rv_inst inst) -{ - return sextract32(inst, 25, 7) << 5 | - extract32(inst, 7, 5); -} - -static int32_t operand_sbimm12(rv_inst inst) -{ - return sextract32(inst, 31, 1) << 12 | - extract32(inst, 25, 6) << 5 | - extract32(inst, 8, 4) << 1 | - extract32(inst, 7, 1) << 11; -} - -static uint32_t operand_cimmshl6(rv_inst inst, rv_isa isa) -{ - int imm = extract32(inst, 12, 1) << 5 | - extract32(inst, 2, 5); - if (isa == rv128) { - imm = imm ? imm : 64; - } - return imm; -} - -static uint32_t operand_cimmshr6(rv_inst inst, rv_isa isa) -{ - int imm = extract32(inst, 12, 1) << 5 | - extract32(inst, 2, 5); - if (isa == rv128) { - imm = imm | (imm & 32) << 1; - imm = imm ? imm : 64; - } - return imm; -} - -static int32_t operand_cimmi(rv_inst inst) -{ - return sextract32(inst, 12, 1) << 5 | - extract32(inst, 2, 5); -} - -static int32_t operand_cimmui(rv_inst inst) -{ - return sextract32(inst, 12, 1) << 17 | - extract32(inst, 2, 5) << 12; -} - -static uint32_t operand_cimmlwsp(rv_inst inst) -{ - return extract32(inst, 12, 1) << 5 | - extract32(inst, 4, 3) << 2 | - extract32(inst, 2, 2) << 6; -} - -static uint32_t operand_cimmldsp(rv_inst inst) -{ - return extract32(inst, 12, 1) << 5 | - extract32(inst, 5, 2) << 3 | - extract32(inst, 2, 3) << 6; -} - -static uint32_t operand_cimmlqsp(rv_inst inst) -{ - return extract32(inst, 12, 1) << 5 | - extract32(inst, 6, 1) << 4 | - extract32(inst, 2, 4) << 6; -} - -static int32_t operand_cimm16sp(rv_inst inst) -{ - return sextract32(inst, 12, 1) << 9 | - extract32(inst, 6, 1) << 4 | - extract32(inst, 5, 1) << 6 | - extract32(inst, 3, 2) << 7 | - extract32(inst, 2, 1) << 5; -} - -static int32_t operand_cimmj(rv_inst inst) -{ - return sextract32(inst, 12, 1) << 11 | - extract32(inst, 11, 1) << 4 | - extract32(inst, 9, 2) << 8 | - extract32(inst, 8, 1) << 10 | - extract32(inst, 7, 1) << 6 | - extract32(inst, 6, 1) << 7 | - extract32(inst, 3, 3) << 1 | - extract32(inst, 2, 1) << 5; -} - -static int32_t operand_cimmb(rv_inst inst) -{ - return sextract32(inst, 12, 1) << 8 | - extract32(inst, 10, 2) << 3 | - extract32(inst, 5, 2) << 6 | - extract32(inst, 3, 2) << 1 | - extract32(inst, 2, 1) << 5; -} - -static uint32_t operand_cimmswsp(rv_inst inst) -{ - return extract32(inst, 9, 4) << 2 | - extract32(inst, 7, 2) << 6; -} - -static uint32_t operand_cimmsdsp(rv_inst inst) -{ - return extract32(inst, 10, 3) << 3 | - extract32(inst, 7, 3) << 6; -} - -static uint32_t operand_cimmsqsp(rv_inst inst) -{ - return extract32(inst, 11, 2) << 4 | - extract32(inst, 7, 4) << 6; -} - -static uint32_t operand_cimm4spn(rv_inst inst) -{ - return extract32(inst, 11, 2) << 4 | - extract32(inst, 7, 4) << 6 | - extract32(inst, 6, 1) << 2 | - extract32(inst, 5, 1) << 3; -} - -static uint32_t operand_cimmw(rv_inst inst) -{ - return extract32(inst, 10, 3) << 3 | - extract32(inst, 6, 1) << 2 | - extract32(inst, 5, 1) << 6; -} - -static uint32_t operand_cimmd(rv_inst inst) -{ - return extract32(inst, 10, 3) << 3 | - extract32(inst, 5, 2) << 6; -} - -static uint32_t operand_cimmq(rv_inst inst) -{ - return extract32(inst, 11, 2) << 4 | - extract32(inst, 10, 1) << 8 | - extract32(inst, 5, 2) << 6; -} - -static int32_t operand_vimm(rv_inst inst) -{ - return sextract32(inst, 15, 5); -} - -static uint32_t operand_vuimm(rv_inst inst) -{ - return extract32(inst, 15, 5); -} - -static uint32_t operand_vzimm11(rv_inst inst) -{ - return extract32(inst, 20, 11); -} - -static uint32_t operand_vzimm10(rv_inst inst) -{ - return extract32(inst, 20, 10); -} - -static uint32_t operand_vzimm6(rv_inst inst) -{ - return extract32(inst, 26, 1) << 5 | - extract32(inst, 15, 5); -} - -static uint32_t operand_bs(rv_inst inst) -{ - return extract32(inst, 30, 2); -} - -static uint32_t operand_rnum(rv_inst inst) -{ - return extract32(inst, 20, 4); -} - -static uint32_t operand_vm(rv_inst inst) -{ - return extract32(inst, 25, 1); -} - -static uint32_t operand_uimm_c_lb(rv_inst inst) -{ - return extract32(inst, 5, 1) << 1 | - extract32(inst, 6, 1); -} - -static uint32_t operand_uimm_c_lh(rv_inst inst) -{ - return extract32(inst, 5, 1) << 1; -} - -static uint32_t operand_zcmp_spimm(rv_inst inst) -{ - return extract32(inst, 2, 2) << 4; -} - -static uint32_t operand_zcmp_rlist(rv_inst inst) -{ - return extract32(inst, 4, 4); -} - -static uint32_t operand_imm6(rv_inst inst) -{ - return extract32(inst, 20, 6); -} - -static uint32_t operand_imm2(rv_inst inst) -{ - return extract32(inst, 25, 2); -} - -static uint32_t operand_immh(rv_inst inst) -{ - return extract32(inst, 26, 6); -} - -static uint32_t operand_imml(rv_inst inst) -{ - return extract32(inst, 20, 6); -} - -static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm) -{ - int xlen_bytes_log2 = isa == rv64 ? 3 : 2; - int regs = rlist == 15 ? 13 : rlist - 3; - uint32_t stack_adj_base = ROUND_UP(regs << xlen_bytes_log2, 16); - return stack_adj_base + spimm; -} - -static uint32_t operand_zcmp_stack_adj(rv_inst inst, rv_isa isa) -{ - return calculate_stack_adj(isa, operand_zcmp_rlist(inst), - operand_zcmp_spimm(inst)); -} - -static uint32_t operand_tbl_index(rv_inst inst) -{ - return extract32(inst, 2, 8); -} - -static uint32_t operand_lpl(rv_inst inst) -{ - return extract32(inst, 12, 20); -} - /* decode operands */ static void decode_inst_operands(rv_decode *dec, rv_isa isa) From 21aa81318d2f8f55f9dcd96aca7ba3eb53844ea0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:52 -0700 Subject: [PATCH 750/860] disas/riscv: Reduce rv_insn to uint32_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since 758dce9c98a, the only possible values for instruction length are 2 and 4. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-3-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 12 +++--------- disas/riscv.h | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index e3fbc26626..012f90fce6 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5491,10 +5491,8 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, return format_inst(24, &dec); } -#define INST_FMT_2 "%04" PRIx64 " " -#define INST_FMT_4 "%08" PRIx64 " " -#define INST_FMT_6 "%012" PRIx64 " " -#define INST_FMT_8 "%016" PRIx64 " " +#define INST_FMT_2 "%04x " +#define INST_FMT_4 "%08x " static int print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) @@ -5530,12 +5528,8 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) case 4: (*info->fprintf_func)(info->stream, INST_FMT_4, inst); break; - case 6: - (*info->fprintf_func)(info->stream, INST_FMT_6, inst); - break; default: - (*info->fprintf_func)(info->stream, INST_FMT_8, inst); - break; + g_assert_not_reached(); } } diff --git a/disas/riscv.h b/disas/riscv.h index 91ada8b531..eabe193b4f 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -11,7 +11,7 @@ /* types */ -typedef uint64_t rv_inst; +typedef uint32_t rv_inst; typedef uint16_t rv_opcode; /* enums */ From fcb7ec69d85e947b6a66b3f7891816dc53670fd5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:53 -0700 Subject: [PATCH 751/860] disas/riscv: Tidy dec initialization in disasm_inst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-4-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 012f90fce6..986dc6b752 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5441,10 +5441,11 @@ static void decode_inst_decompress(rv_decode *dec, rv_isa isa) static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, const RISCVCPUConfig *cfg) { - rv_decode dec = { 0 }; - dec.pc = pc; - dec.inst = inst; - dec.cfg = cfg; + rv_decode dec = { + .pc = pc, + .inst = inst, + .cfg = cfg, + }; static const struct { bool (*guard_func)(const RISCVCPUConfig *); From 2d64d3a6a1404f2f09c0d43ce715cd4b001f8f26 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:54 -0700 Subject: [PATCH 752/860] disas/riscv: Pass rv_opcode_data pointer to decode_inst_operands Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-5-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 986dc6b752..af43940732 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4595,11 +4595,12 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) /* decode operands */ -static void decode_inst_operands(rv_decode *dec, rv_isa isa) +static void decode_inst_operands(rv_decode *dec, rv_isa isa, + const rv_opcode_data *op) { - const rv_opcode_data *opcode_data = dec->opcode_data; rv_inst inst = dec->inst; - dec->codec = opcode_data[dec->op].codec; + + dec->codec = op->codec; switch (dec->codec) { case rv_codec_none: dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; @@ -5468,6 +5469,8 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, { has_xlrbr_p, rv_xlrbr_opcode_data, decode_xlrbr }, }; + const rv_opcode_data *op; + for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { bool (*guard_func)(const RISCVCPUConfig *) = decoders[i].guard_func; const rv_opcode_data *opcode_data = decoders[i].opcode_data; @@ -5486,7 +5489,8 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, dec.opcode_data = rvi_opcode_data; } - decode_inst_operands(&dec, isa); + op = &dec.opcode_data[dec.op]; + decode_inst_operands(&dec, isa, op); decode_inst_decompress(&dec, isa); decode_inst_lift_pseudo(&dec); return format_inst(24, &dec); From 3d3abbb1469756811f50811aca27883e30d00ac6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:55 -0700 Subject: [PATCH 753/860] disas/riscv: Pass rv_opcode_data pointer to/from decode_inst_decompress Unify 4 functions, sharing code. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-6-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 70 ++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 49 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index af43940732..47fae7316c 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5377,64 +5377,36 @@ static void decode_inst_lift_pseudo(rv_decode *dec) /* decompress instruction */ -static void decode_inst_decompress_rv32(rv_decode *dec) +static const rv_opcode_data *decode_inst_decompress(rv_decode *dec, rv_isa isa, + const rv_opcode_data *op) { - const rv_opcode_data *opcode_data = dec->opcode_data; - int decomp_op = opcode_data[dec->op].decomp_rv32; - if (decomp_op != rv_op_illegal) { - if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) - && dec->imm == 0) { - dec->op = rv_op_illegal; - } else { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; - } - } -} + int decomp_op; -static void decode_inst_decompress_rv64(rv_decode *dec) -{ - const rv_opcode_data *opcode_data = dec->opcode_data; - int decomp_op = opcode_data[dec->op].decomp_rv64; - if (decomp_op != rv_op_illegal) { - if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) - && dec->imm == 0) { - dec->op = rv_op_illegal; - } else { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; - } - } -} - -static void decode_inst_decompress_rv128(rv_decode *dec) -{ - const rv_opcode_data *opcode_data = dec->opcode_data; - int decomp_op = opcode_data[dec->op].decomp_rv128; - if (decomp_op != rv_op_illegal) { - if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz) - && dec->imm == 0) { - dec->op = rv_op_illegal; - } else { - dec->op = decomp_op; - dec->codec = opcode_data[decomp_op].codec; - } - } -} - -static void decode_inst_decompress(rv_decode *dec, rv_isa isa) -{ switch (isa) { case rv32: - decode_inst_decompress_rv32(dec); + decomp_op = op->decomp_rv32; break; case rv64: - decode_inst_decompress_rv64(dec); + decomp_op = op->decomp_rv64; break; case rv128: - decode_inst_decompress_rv128(dec); + decomp_op = op->decomp_rv128; break; + default: + g_assert_not_reached(); } + + if (decomp_op != rv_op_illegal) { + if ((op->decomp_data & rvcd_imm_nz) && dec->imm == 0) { + dec->opcode_data = rvi_opcode_data; + dec->op = rv_op_illegal; + } else { + dec->op = decomp_op; + } + op = &dec->opcode_data[decomp_op]; + dec->codec = op->codec; + } + return op; } /* disassemble instruction */ @@ -5491,7 +5463,7 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, op = &dec.opcode_data[dec.op]; decode_inst_operands(&dec, isa, op); - decode_inst_decompress(&dec, isa); + op = decode_inst_decompress(&dec, isa, op); decode_inst_lift_pseudo(&dec); return format_inst(24, &dec); } From f8bc5f5635f67dc21919454bdca4a9f917e5337d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:56 -0700 Subject: [PATCH 754/860] disas/riscv: Pass rv_opcode_data pointer to/from decode_inst_lift_pseudo Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-7-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 47fae7316c..0be2f07612 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5358,21 +5358,22 @@ static GString *format_inst(size_t tab, rv_decode *dec) /* lift instruction to pseudo-instruction */ -static void decode_inst_lift_pseudo(rv_decode *dec) +static const rv_opcode_data *decode_inst_lift_pseudo(rv_decode *dec, + const rv_opcode_data *op) { - const rv_opcode_data *opcode_data = dec->opcode_data; - const rv_comp_data *comp_data = opcode_data[dec->op].pseudo; - if (!comp_data) { - return; - } - while (comp_data->constraints) { - if (check_constraints(dec, comp_data->constraints)) { - dec->op = comp_data->op; - dec->codec = opcode_data[dec->op].codec; - return; + const rv_comp_data *comp_data = op->pseudo; + if (comp_data) { + while (comp_data->constraints) { + if (check_constraints(dec, comp_data->constraints)) { + dec->op = comp_data->op; + op = &dec->opcode_data[dec->op]; + dec->codec = op->codec; + break; + } + comp_data++; } - comp_data++; } + return op; } /* decompress instruction */ @@ -5464,7 +5465,7 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, op = &dec.opcode_data[dec.op]; decode_inst_operands(&dec, isa, op); op = decode_inst_decompress(&dec, isa, op); - decode_inst_lift_pseudo(&dec); + op = decode_inst_lift_pseudo(&dec, op); return format_inst(24, &dec); } From 7276d0fbe60d1c757ee75a52a2c33b1835b13a73 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:57 -0700 Subject: [PATCH 755/860] disas/riscv: Pass rv_opcode_data pointer to format_inst Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-8-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 0be2f07612..70ea41f125 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5111,17 +5111,16 @@ static size_t inst_length(rv_inst inst) /* format instruction */ -static GString *format_inst(size_t tab, rv_decode *dec) +static GString *format_inst(size_t tab, rv_decode *dec, + const rv_opcode_data *op) { - const rv_opcode_data *opcode_data = dec->opcode_data; GString *buf = g_string_sized_new(64); - const char *fmt; + const char *fmt = op->format; - fmt = opcode_data[dec->op].format; while (*fmt) { switch (*fmt) { case 'O': - g_string_append(buf, opcode_data[dec->op].name); + g_string_append(buf, op->name); break; case '(': case ',': @@ -5466,7 +5465,7 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, decode_inst_operands(&dec, isa, op); op = decode_inst_decompress(&dec, isa, op); op = decode_inst_lift_pseudo(&dec, op); - return format_inst(24, &dec); + return format_inst(24, &dec, op); } #define INST_FMT_2 "%04x " From aae0ef66d463f9dd47624d249f975904a1ff2c0a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:58 -0700 Subject: [PATCH 756/860] disas/riscv: Tidy disasm_inst main loop Unroll first iteration, so that always_true_p is not used, Drop some local variables and use 'decoders' directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-9-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 63 +++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 70ea41f125..9880b8de20 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5419,41 +5419,40 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, .inst = inst, .cfg = cfg, }; - - static const struct { - bool (*guard_func)(const RISCVCPUConfig *); - const rv_opcode_data *opcode_data; - void (*decode_func)(rv_decode *, rv_isa); - } decoders[] = { - { always_true_p, rvi_opcode_data, decode_inst_opcode }, - { has_xtheadba_p, xthead_opcode_data, decode_xtheadba }, - { has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb }, - { has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs }, - { has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo }, - { has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov }, - { has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx }, - { has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv }, - { has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac }, - { has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx }, - { has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair }, - { has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync }, - { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops }, - { has_xlrbr_p, rv_xlrbr_opcode_data, decode_xlrbr }, - }; - const rv_opcode_data *op; - for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { - bool (*guard_func)(const RISCVCPUConfig *) = decoders[i].guard_func; - const rv_opcode_data *opcode_data = decoders[i].opcode_data; - void (*decode_func)(rv_decode *, rv_isa) = decoders[i].decode_func; + dec.opcode_data = rvi_opcode_data; + decode_inst_opcode(&dec, isa); - /* always_true_p don't dereference cfg */ - if (((i == 0) || cfg) && guard_func(cfg)) { - dec.opcode_data = opcode_data; - decode_func(&dec, isa); - if (dec.op != rv_op_illegal) - break; + if (dec.op == rv_op_illegal && cfg) { + static const struct { + bool (*guard_func)(const RISCVCPUConfig *); + const rv_opcode_data *opcode_data; + void (*decode_func)(rv_decode *, rv_isa); + } decoders[] = { + { has_xtheadba_p, xthead_opcode_data, decode_xtheadba }, + { has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb }, + { has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs }, + { has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo }, + { has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov }, + { has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx }, + { has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv }, + { has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac }, + { has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx }, + { has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair }, + { has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync }, + { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops }, + { has_xlrbr_p, rv_xlrbr_opcode_data, decode_xlrbr }, + }; + + for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { + if (decoders[i].guard_func(cfg)) { + dec.opcode_data = decoders[i].opcode_data; + decoders[i].decode_func(&dec, isa); + if (dec.op != rv_op_illegal) { + break; + } + } } } From 42031f21f9c8ee999dc6a1b1589a753df47bde37 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:30:59 -0700 Subject: [PATCH 757/860] disas/riscv: Set dec->opcode_data in decode function This allows each opcode table to be private to the decode file. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-10-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-xlrbr.c | 4 +++- disas/riscv-xlrbr.h | 2 -- disas/riscv-xthead.c | 13 ++++++++++++- disas/riscv-xthead.h | 2 -- disas/riscv-xventana.c | 3 ++- disas/riscv-xventana.h | 2 -- disas/riscv.c | 34 +++++++++++++++++----------------- 7 files changed, 34 insertions(+), 26 deletions(-) diff --git a/disas/riscv-xlrbr.c b/disas/riscv-xlrbr.c index 57cb434523..f61028fce9 100644 --- a/disas/riscv-xlrbr.c +++ b/disas/riscv-xlrbr.c @@ -24,7 +24,7 @@ typedef enum { rv_op_crc32c_d = 8, } rv_xlrbr_op; -const rv_opcode_data rv_xlrbr_opcode_data[] = { +static const rv_opcode_data xlrbr_opcode_data[] = { { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, { "crc32.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, { "crc32.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, @@ -75,5 +75,7 @@ void decode_xlrbr(rv_decode *dec, rv_isa isa) } break; } + + dec->opcode_data = xlrbr_opcode_data; dec->op = op; } diff --git a/disas/riscv-xlrbr.h b/disas/riscv-xlrbr.h index 939a69ea6d..ebf3d4c133 100644 --- a/disas/riscv-xlrbr.h +++ b/disas/riscv-xlrbr.h @@ -12,8 +12,6 @@ #include "disas/riscv.h" -extern const rv_opcode_data rv_xlrbr_opcode_data[]; - void decode_xlrbr(rv_decode *, rv_isa); #endif /* DISAS_RISCV_XLRBR_H */ diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c index 2f2ddb8dd9..96f80bc8e5 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -127,7 +127,7 @@ typedef enum { rv_op_th_sync_s, } rv_xthead_op; -const rv_opcode_data xthead_opcode_data[] = { +static const rv_opcode_data xthead_opcode_data[] = { { "th.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, /* XTheadBa */ { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, @@ -272,6 +272,7 @@ void decode_xtheadba(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -330,6 +331,7 @@ void decode_xtheadbb(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -356,6 +358,7 @@ void decode_xtheadbs(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -434,6 +437,7 @@ void decode_xtheadcmo(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -461,6 +465,7 @@ void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -498,6 +503,7 @@ void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -533,6 +539,7 @@ void decode_xtheadfmv(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -564,6 +571,7 @@ void decode_xtheadmac(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -638,6 +646,7 @@ void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -672,6 +681,7 @@ void decode_xtheadmempair(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } @@ -706,5 +716,6 @@ void decode_xtheadsync(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = xthead_opcode_data; dec->op = op; } diff --git a/disas/riscv-xthead.h b/disas/riscv-xthead.h index fcd42746e7..0c65c8e0af 100644 --- a/disas/riscv-xthead.h +++ b/disas/riscv-xthead.h @@ -11,8 +11,6 @@ #include "disas/riscv.h" -extern const rv_opcode_data xthead_opcode_data[]; - void decode_xtheadba(rv_decode *, rv_isa); void decode_xtheadbb(rv_decode *, rv_isa); void decode_xtheadbs(rv_decode *, rv_isa); diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c index cd694f15f3..4539173d95 100644 --- a/disas/riscv-xventana.c +++ b/disas/riscv-xventana.c @@ -14,7 +14,7 @@ typedef enum { ventana_op_vt_maskcn = 2, } rv_ventana_op; -const rv_opcode_data ventana_opcode_data[] = { +static const rv_opcode_data ventana_opcode_data[] = { { "vt.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, @@ -38,5 +38,6 @@ void decode_xventanacondops(rv_decode *dec, rv_isa isa) break; } + dec->opcode_data = ventana_opcode_data; dec->op = op; } diff --git a/disas/riscv-xventana.h b/disas/riscv-xventana.h index 72be9ffa16..1006c3be00 100644 --- a/disas/riscv-xventana.h +++ b/disas/riscv-xventana.h @@ -11,8 +11,6 @@ #include "disas/riscv.h" -extern const rv_opcode_data ventana_opcode_data[]; - void decode_xventanacondops(rv_decode*, rv_isa); #endif /* DISAS_RISCV_XVENTANA_H */ diff --git a/disas/riscv.c b/disas/riscv.c index 9880b8de20..eb6d132a2c 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1656,7 +1656,7 @@ static uint32_t operand_lpl(rv_inst inst) /* instruction metadata */ -const rv_opcode_data rvi_opcode_data[] = { +static const rv_opcode_data rvi_opcode_data[] = { { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, { "lui", rv_codec_u, rv_fmt_rd_uimm, NULL, 0, 0, 0 }, { "auipc", rv_codec_u, rv_fmt_rd_uoffset, NULL, 0, 0, 0 }, @@ -2948,6 +2948,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; + switch ((inst >> 0) & 0b11) { case 0: switch ((inst >> 13) & 0b111) { @@ -4590,6 +4591,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; } + + dec->opcode_data = rvi_opcode_data; dec->op = op; } @@ -5421,33 +5424,30 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, }; const rv_opcode_data *op; - dec.opcode_data = rvi_opcode_data; decode_inst_opcode(&dec, isa); if (dec.op == rv_op_illegal && cfg) { static const struct { bool (*guard_func)(const RISCVCPUConfig *); - const rv_opcode_data *opcode_data; void (*decode_func)(rv_decode *, rv_isa); } decoders[] = { - { has_xtheadba_p, xthead_opcode_data, decode_xtheadba }, - { has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb }, - { has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs }, - { has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo }, - { has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov }, - { has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx }, - { has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv }, - { has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac }, - { has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx }, - { has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair }, - { has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync }, - { has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops }, - { has_xlrbr_p, rv_xlrbr_opcode_data, decode_xlrbr }, + { has_xtheadba_p, decode_xtheadba }, + { has_xtheadbb_p, decode_xtheadbb }, + { has_xtheadbs_p, decode_xtheadbs }, + { has_xtheadcmo_p, decode_xtheadcmo }, + { has_xtheadcondmov_p, decode_xtheadcondmov }, + { has_xtheadfmemidx_p, decode_xtheadfmemidx }, + { has_xtheadfmv_p, decode_xtheadfmv }, + { has_xtheadmac_p, decode_xtheadmac }, + { has_xtheadmemidx_p, decode_xtheadmemidx }, + { has_xtheadmempair_p, decode_xtheadmempair }, + { has_xtheadsync_p, decode_xtheadsync }, + { has_XVentanaCondOps_p, decode_xventanacondops }, + { has_xlrbr_p, decode_xlrbr }, }; for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { if (decoders[i].guard_func(cfg)) { - dec.opcode_data = decoders[i].opcode_data; decoders[i].decode_func(&dec, isa); if (dec.op != rv_op_illegal) { break; From 7de6afd531867f9f479332a0d6f5a916ba6b8b66 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:00 -0700 Subject: [PATCH 758/860] disas/riscv: Drop explicit zero of rv_opcode_data fields Mechanical s/, NULL, 0, 0, 0 }/ }/ s/, 0, 0, 0 }/ }/ s/, NULL, 0, 0 }/ }/ s/, NULL, 0 }/ }/ Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-11-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-xlrbr.c | 18 +- disas/riscv-xthead.c | 210 ++--- disas/riscv-xventana.c | 6 +- disas/riscv.c | 1826 ++++++++++++++++++++-------------------- 4 files changed, 1030 insertions(+), 1030 deletions(-) diff --git a/disas/riscv-xlrbr.c b/disas/riscv-xlrbr.c index f61028fce9..d0092e9edd 100644 --- a/disas/riscv-xlrbr.c +++ b/disas/riscv-xlrbr.c @@ -25,15 +25,15 @@ typedef enum { } rv_xlrbr_op; static const rv_opcode_data xlrbr_opcode_data[] = { - { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - { "crc32.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32.w", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.w", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "crc32c.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "illegal", rv_codec_illegal, rv_fmt_none }, + { "crc32.b", rv_codec_r, rv_fmt_rd_rs1 }, + { "crc32.h", rv_codec_r, rv_fmt_rd_rs1 }, + { "crc32.w", rv_codec_r, rv_fmt_rd_rs1 }, + { "crc32.d", rv_codec_r, rv_fmt_rd_rs1 }, + { "crc32c.b", rv_codec_r, rv_fmt_rd_rs1 }, + { "crc32c.h", rv_codec_r, rv_fmt_rd_rs1 }, + { "crc32c.w", rv_codec_r, rv_fmt_rd_rs1 }, + { "crc32c.d", rv_codec_r, rv_fmt_rd_rs1 }, }; void decode_xlrbr(rv_decode *dec, rv_isa isa) diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c index 96f80bc8e5..757d6ff39f 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -128,122 +128,122 @@ typedef enum { } rv_xthead_op; static const rv_opcode_data xthead_opcode_data[] = { - { "th.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.illegal", rv_codec_illegal, rv_fmt_none }, /* XTheadBa */ - { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, /* XTheadBb */ - { "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, - { "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, - { "th.ff0", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.ff1", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.rev", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.revw", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, + { "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm }, + { "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm }, + { "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml }, + { "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml }, + { "th.ff0", rv_codec_r2, rv_fmt_rd_rs1 }, + { "th.ff1", rv_codec_r2, rv_fmt_rd_rs1 }, + { "th.rev", rv_codec_r2, rv_fmt_rd_rs1 }, + { "th.revw", rv_codec_r2, rv_fmt_rd_rs1 }, + { "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1 }, /* XTheadBs */ - { "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, + { "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm }, /* XTheadCmo */ - { "th.dcache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.dcache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.dcache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.dcache.cpa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.civa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.csw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cisw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.isw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.dcache.cval1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.icache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.icache.ialls", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.icache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.icache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "th.l2cache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.l2cache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.l2cache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.dcache.call", rv_codec_none, rv_fmt_none }, + { "th.dcache.ciall", rv_codec_none, rv_fmt_none }, + { "th.dcache.iall", rv_codec_none, rv_fmt_none }, + { "th.dcache.cpa", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.cipa", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.ipa", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.cva", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.civa", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.iva", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.csw", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.cisw", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.isw", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1 }, + { "th.dcache.cval1", rv_codec_r, rv_fmt_rs1 }, + { "th.icache.iall", rv_codec_none, rv_fmt_none }, + { "th.icache.ialls", rv_codec_none, rv_fmt_none }, + { "th.icache.ipa", rv_codec_r, rv_fmt_rs1 }, + { "th.icache.iva", rv_codec_r, rv_fmt_rs1 }, + { "th.l2cache.call", rv_codec_none, rv_fmt_none }, + { "th.l2cache.ciall", rv_codec_none, rv_fmt_none }, + { "th.l2cache.iall", rv_codec_none, rv_fmt_none }, /* XTheadCondMov */ - { "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2 }, /* XTheadFMemIdx */ - { "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, + { "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, + { "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, + { "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, + { "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, + { "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, + { "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, + { "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, /* XTheadFmv */ - { "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, + { "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1 }, + { "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1 }, /* XTheadMac */ - { "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, /* XTheadMemIdx */ - { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 }, - { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 }, - { "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, - { "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 }, + { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml }, + { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, + { "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, + { "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, /* XTheadMemPair */ - { "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, - { "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 }, + { "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm }, + { "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm }, + { "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm }, + { "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm }, + { "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm }, /* XTheadSync */ - { "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 }, - { "th.sync", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.sync.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.sync.is", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "th.sync.s", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2 }, + { "th.sync", rv_codec_none, rv_fmt_none }, + { "th.sync.i", rv_codec_none, rv_fmt_none }, + { "th.sync.is", rv_codec_none, rv_fmt_none }, + { "th.sync.s", rv_codec_none, rv_fmt_none }, }; void decode_xtheadba(rv_decode *dec, rv_isa isa) diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c index 4539173d95..4fb9a29d0b 100644 --- a/disas/riscv-xventana.c +++ b/disas/riscv-xventana.c @@ -15,9 +15,9 @@ typedef enum { } rv_ventana_op; static const rv_opcode_data ventana_opcode_data[] = { - { "vt.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, + { "vt.illegal", rv_codec_illegal, rv_fmt_none }, + { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2 }, }; void decode_xventanacondops(rv_decode *dec, rv_isa isa) diff --git a/disas/riscv.c b/disas/riscv.c index eb6d132a2c..ec40abb618 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1657,233 +1657,233 @@ static uint32_t operand_lpl(rv_inst inst) /* instruction metadata */ static const rv_opcode_data rvi_opcode_data[] = { - { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 }, - { "lui", rv_codec_u, rv_fmt_rd_uimm, NULL, 0, 0, 0 }, - { "auipc", rv_codec_u, rv_fmt_rd_uoffset, NULL, 0, 0, 0 }, - { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal, 0, 0, 0 }, - { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr, 0, 0, 0 }, - { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq, 0, 0, 0 }, - { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne, 0, 0, 0 }, - { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt, 0, 0, 0 }, - { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge, 0, 0, 0 }, - { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bltu, 0, 0, 0 }, - { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bgeu, 0, 0, 0 }, - { "lb", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lh", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lw", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lbu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lhu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "sb", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "sh", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "sw", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi, 0, 0, 0 }, - { "slti", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu, 0, 0, 0 }, - { "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori, 0, 0, 0 }, - { "ori", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "andi", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub, 0, 0, 0 }, - { "sll", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt, 0, 0, 0 }, - { "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu, 0, 0, 0 }, - { "xor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sra", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "or", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "and", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "fence", rv_codec_r_f, rv_fmt_pred_succ, NULL, 0, 0, 0 }, - { "fence.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "lwu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "ld", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "sd", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw, 0, 0, 0 }, - { "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "addw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw, 0, 0, 0 }, - { "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "ldu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "lq", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 }, - { "sq", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 }, - { "addid", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "addd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "subd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "slld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "srad", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "div", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rem", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "muld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "divud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "remud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, - { "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, - { "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 }, - { "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "ecall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "ebreak", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "uret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "sret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "hret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "mret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "dret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "sfence.vm", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 }, - { "wfi", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrw, 0, 0, 0 }, - { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs, 0, 0, 0 }, - { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1, NULL, 0, 0, 0 }, - { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi, 0, 0, 0 }, - { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 }, - { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 }, - { "flw", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s, 0, 0, 0 }, - { "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s, 0, 0, 0 }, - { "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s, 0, 0, 0 }, - { "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fclass.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fld", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d, 0, 0, 0 }, - { "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d, 0, 0, 0 }, - { "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d, 0, 0, 0 }, - { "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fclass.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "flq", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 }, - { "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q, 0, 0, 0 }, - { "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q, 0, 0, 0 }, - { "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q, 0, 0, 0 }, - { "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fclass.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, + { "illegal", rv_codec_illegal, rv_fmt_none }, + { "lui", rv_codec_u, rv_fmt_rd_uimm }, + { "auipc", rv_codec_u, rv_fmt_rd_uoffset }, + { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal }, + { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr }, + { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq }, + { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne }, + { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt }, + { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge }, + { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bltu }, + { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bgeu }, + { "lb", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "lh", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "lw", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "lbu", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "lhu", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "sb", rv_codec_s, rv_fmt_rs2_offset_rs1 }, + { "sh", rv_codec_s, rv_fmt_rs2_offset_rs1 }, + { "sw", rv_codec_s, rv_fmt_rs2_offset_rs1 }, + { "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi }, + { "slti", rv_codec_i, rv_fmt_rd_rs1_imm }, + { "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu }, + { "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori }, + { "ori", rv_codec_i, rv_fmt_rd_rs1_imm }, + { "andi", rv_codec_i, rv_fmt_rd_rs1_imm }, + { "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub }, + { "sll", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt }, + { "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu }, + { "xor", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "srl", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sra", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "or", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "and", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "fence", rv_codec_r_f, rv_fmt_pred_succ }, + { "fence.i", rv_codec_none, rv_fmt_none }, + { "lwu", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "ld", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "sd", rv_codec_s, rv_fmt_rs2_offset_rs1 }, + { "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw }, + { "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, + { "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, + { "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, + { "addw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw }, + { "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "ldu", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "lq", rv_codec_i, rv_fmt_rd_offset_rs1 }, + { "sq", rv_codec_s, rv_fmt_rs2_offset_rs1 }, + { "addid", rv_codec_i, rv_fmt_rd_rs1_imm }, + { "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, + { "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, + { "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, + { "addd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "subd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "slld", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "srld", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "srad", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mul", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "div", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "divu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "rem", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "remu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "divw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "remw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "muld", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "divd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "divud", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "remd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "remud", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1 }, + { "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1 }, + { "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1 }, + { "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "ecall", rv_codec_none, rv_fmt_none }, + { "ebreak", rv_codec_none, rv_fmt_none }, + { "uret", rv_codec_none, rv_fmt_none }, + { "sret", rv_codec_none, rv_fmt_none }, + { "hret", rv_codec_none, rv_fmt_none }, + { "mret", rv_codec_none, rv_fmt_none }, + { "dret", rv_codec_none, rv_fmt_none }, + { "sfence.vm", rv_codec_r, rv_fmt_rs1 }, + { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2 }, + { "wfi", rv_codec_none, rv_fmt_none }, + { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrw }, + { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs }, + { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, + { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi }, + { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, + { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, + { "flw", rv_codec_i, rv_fmt_frd_offset_rs1 }, + { "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1 }, + { "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s }, + { "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s }, + { "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s }, + { "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1 }, + { "fclass.s", rv_codec_r, rv_fmt_rd_frs1 }, + { "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1 }, + { "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fld", rv_codec_i, rv_fmt_frd_offset_rs1 }, + { "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1 }, + { "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d }, + { "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d }, + { "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d }, + { "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fclass.d", rv_codec_r, rv_fmt_rd_frs1 }, + { "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1 }, + { "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1 }, + { "flq", rv_codec_i, rv_fmt_frd_offset_rs1 }, + { "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1 }, + { "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, + { "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, + { "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q }, + { "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q }, + { "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q }, + { "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fclass.q", rv_codec_r, rv_fmt_rd_frs1 }, + { "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, + { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1 }, + { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1 }, { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi, rvcd_imm_nz }, { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, @@ -1970,696 +1970,696 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, - { "nop", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, - { "mv", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "not", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "neg", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "negw", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "sext.w", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "seqz", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "snez", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "sltz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "sgtz", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "fmv.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fabs.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fneg.s", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fmv.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fabs.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fneg.d", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fmv.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fabs.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "fneg.q", rv_codec_r, rv_fmt_frd_frs1, NULL, 0, 0, 0 }, - { "beqz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "bnez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "blez", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 }, - { "bgez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "bltz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 }, - { "bgtz", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 }, - { "ble", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "bleu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "bgt", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "bgtu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 }, - { "j", rv_codec_uj, rv_fmt_offset, NULL, 0, 0, 0 }, - { "ret", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 }, - { "jr", rv_codec_i, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "rdcycle", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdtime", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdinstret", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdcycleh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdtimeh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "rdinstreth", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "frcsr", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "frrm", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "frflags", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 }, - { "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, - { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 }, - { "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "clz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "ctz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "cpop", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "sext.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "sext.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "orn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "andn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rol", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "ror", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "min", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "minu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "max", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "clzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "ctzw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "cpopw", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "rev8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "zext.h", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 }, - { "orc.b", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "bset", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "binv", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "bext", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum, NULL, 0, 0, 0 }, - { "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64im", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha256sig0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha256sig1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha256sum0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha256sum1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "sm3p0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sm3p1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs, NULL, 0, 0, 0 }, - { "brev8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "pack", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "packh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "packw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "unzip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "zip", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "xperm8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 }, - { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm, NULL, 0, 0, 0 }, - { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm, NULL, 0, 0, 0 }, - { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, - { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, - { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm, NULL, 0, 0, 0 }, - { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1, NULL, 0, 0, 0 }, - { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, - { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm, NULL, 0, 0, 0 }, - { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl, NULL, 0, 0, 0 }, - { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl, NULL, 0, 0, 0 }, - { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl, NULL, 0, 0, 0 }, - { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm, NULL, 0, 0, 0 }, - { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm, NULL, 0, 0, 0 }, - { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl, NULL, 0, 0, 0 }, - { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, - { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, - { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm, NULL, 0, 0, 0 }, - { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vid.v", rv_codec_v_r, rv_fmt_vd_vm, NULL, 0, 0, 0 }, - { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2, NULL, 0, 0, 0 }, - { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1, NULL, 0, 0, 0 }, - { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2, NULL, 0, 0, 0 }, - { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1, NULL, 0, 0, 0 }, - { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli, NULL, 0, 0, 0 }, - { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli, NULL, 0, 0, 0 }, - { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.not", rv_codec_zcb_ext, rv_fmt_rd, NULL, 0 }, - { "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2, NULL, 0, 0 }, - { "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst, NULL, 0, 0, 0 }, - { "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist, NULL, 0, 0 }, - { "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0 }, - { "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0, 0 }, - { "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist, NULL, 0, 0 }, - { "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2, NULL, 0, 0, 0 }, - { "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 }, - { "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 }, - { "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 }, - { "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 }, - { "flh", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 }, - { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 }, - { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 }, - { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fli.s", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fli.d", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fli.q", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fli.h", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 }, - { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 }, - { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 }, - { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 }, - { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, - { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 }, - { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 }, - { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 }, - { "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 }, - { "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1, NULL, 0, 0, 0 }, - { "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm, NULL, 0, 0, 0 }, - { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2, NULL, 0, 0, 0 }, - { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm, NULL, 0, 0, 0 }, - { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, NULL, 0, 0, 0 }, - { "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm, NULL, 0, 0, 0 }, - { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "mop.r.0", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.1", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.2", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.3", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.4", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.5", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.6", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.7", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.8", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.9", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.10", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.11", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.12", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.13", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.14", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.15", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.16", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.17", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.18", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.19", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.20", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.21", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.22", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.23", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.24", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.25", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.26", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.27", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.28", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.29", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.30", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.r.31", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0 }, - { "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 }, - { "c.mop.1", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.3", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.5", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.7", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.9", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.11", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.13", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "c.mop.15", rv_codec_ci_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "wrs.sto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "wrs.nto", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, - { "lpad", rv_codec_lp, rv_fmt_imm, NULL, 0, 0, 0 }, - { "sspush", rv_codec_r, rv_fmt_rs2, NULL, 0, 0, 0 }, - { "sspopchk", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "ssrdp", rv_codec_r, rv_fmt_rd, NULL, 0, 0, 0 }, - { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, - { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 }, + { "nop", rv_codec_i, rv_fmt_none }, + { "mv", rv_codec_i, rv_fmt_rd_rs1 }, + { "not", rv_codec_i, rv_fmt_rd_rs1 }, + { "neg", rv_codec_r, rv_fmt_rd_rs2 }, + { "negw", rv_codec_r, rv_fmt_rd_rs2 }, + { "sext.w", rv_codec_i, rv_fmt_rd_rs1 }, + { "seqz", rv_codec_i, rv_fmt_rd_rs1 }, + { "snez", rv_codec_r, rv_fmt_rd_rs2 }, + { "sltz", rv_codec_r, rv_fmt_rd_rs1 }, + { "sgtz", rv_codec_r, rv_fmt_rd_rs2 }, + { "fmv.s", rv_codec_r, rv_fmt_frd_frs1 }, + { "fabs.s", rv_codec_r, rv_fmt_frd_frs1 }, + { "fneg.s", rv_codec_r, rv_fmt_frd_frs1 }, + { "fmv.d", rv_codec_r, rv_fmt_frd_frs1 }, + { "fabs.d", rv_codec_r, rv_fmt_frd_frs1 }, + { "fneg.d", rv_codec_r, rv_fmt_frd_frs1 }, + { "fmv.q", rv_codec_r, rv_fmt_frd_frs1 }, + { "fabs.q", rv_codec_r, rv_fmt_frd_frs1 }, + { "fneg.q", rv_codec_r, rv_fmt_frd_frs1 }, + { "beqz", rv_codec_sb, rv_fmt_rs1_offset }, + { "bnez", rv_codec_sb, rv_fmt_rs1_offset }, + { "blez", rv_codec_sb, rv_fmt_rs2_offset }, + { "bgez", rv_codec_sb, rv_fmt_rs1_offset }, + { "bltz", rv_codec_sb, rv_fmt_rs1_offset }, + { "bgtz", rv_codec_sb, rv_fmt_rs2_offset }, + { "ble", rv_codec_sb, rv_fmt_rs2_rs1_offset }, + { "bleu", rv_codec_sb, rv_fmt_rs2_rs1_offset }, + { "bgt", rv_codec_sb, rv_fmt_rs2_rs1_offset }, + { "bgtu", rv_codec_sb, rv_fmt_rs2_rs1_offset }, + { "j", rv_codec_uj, rv_fmt_offset }, + { "ret", rv_codec_i, rv_fmt_none }, + { "jr", rv_codec_i, rv_fmt_rs1 }, + { "rdcycle", rv_codec_i_csr, rv_fmt_rd }, + { "rdtime", rv_codec_i_csr, rv_fmt_rd }, + { "rdinstret", rv_codec_i_csr, rv_fmt_rd }, + { "rdcycleh", rv_codec_i_csr, rv_fmt_rd }, + { "rdtimeh", rv_codec_i_csr, rv_fmt_rd }, + { "rdinstreth", rv_codec_i_csr, rv_fmt_rd }, + { "frcsr", rv_codec_i_csr, rv_fmt_rd }, + { "frrm", rv_codec_i_csr, rv_fmt_rd }, + { "frflags", rv_codec_i_csr, rv_fmt_rd }, + { "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1 }, + { "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1 }, + { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1 }, + { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm }, + { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm }, + { "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, + { "clz", rv_codec_r, rv_fmt_rd_rs1 }, + { "ctz", rv_codec_r, rv_fmt_rd_rs1 }, + { "cpop", rv_codec_r, rv_fmt_rd_rs1 }, + { "sext.h", rv_codec_r, rv_fmt_rd_rs1 }, + { "sext.b", rv_codec_r, rv_fmt_rd_rs1 }, + { "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "orn", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "andn", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "rol", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "ror", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "min", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "minu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "max", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "clzw", rv_codec_r, rv_fmt_rd_rs1 }, + { "ctzw", rv_codec_r, rv_fmt_rd_rs1 }, + { "cpopw", rv_codec_r, rv_fmt_rd_rs1 }, + { "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, + { "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "rev8", rv_codec_r, rv_fmt_rd_rs1 }, + { "zext.h", rv_codec_r, rv_fmt_rd_rs1 }, + { "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, + { "orc.b", rv_codec_r, rv_fmt_rd_rs1 }, + { "bset", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "binv", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "bext", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, + { "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, + { "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, + { "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, + { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum }, + { "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "aes64im", rv_codec_r, rv_fmt_rd_rs1 }, + { "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha256sig0", rv_codec_r, rv_fmt_rd_rs1 }, + { "sha256sig1", rv_codec_r, rv_fmt_rd_rs1 }, + { "sha256sum0", rv_codec_r, rv_fmt_rd_rs1 }, + { "sha256sum1", rv_codec_r, rv_fmt_rd_rs1 }, + { "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "sm3p0", rv_codec_r, rv_fmt_rd_rs1 }, + { "sm3p1", rv_codec_r, rv_fmt_rd_rs1 }, + { "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, + { "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, + { "brev8", rv_codec_r, rv_fmt_rd_rs1 }, + { "pack", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "packh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "packw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "unzip", rv_codec_r, rv_fmt_rd_rs1 }, + { "zip", rv_codec_r, rv_fmt_rd_rs1 }, + { "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "xperm8", rv_codec_r, rv_fmt_rd_rs1 }, + { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, + { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, + { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, + { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, + { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, + { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl }, + { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, + { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, + { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl }, + { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, + { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, + { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, + { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, + { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, + { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1 }, + { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1 }, + { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm }, + { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, + { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, + { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl }, + { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, + { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, + { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl }, + { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1 }, + { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm }, + { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm }, + { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vid.v", rv_codec_v_r, rv_fmt_vd_vm }, + { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2 }, + { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1 }, + { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2 }, + { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1 }, + { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, + { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli }, + { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli }, + { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd }, + { "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd }, + { "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd }, + { "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd }, + { "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd }, + { "c.not", rv_codec_zcb_ext, rv_fmt_rd }, + { "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2 }, + { "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst }, + { "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst }, + { "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst }, + { "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst }, + { "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst }, + { "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist }, + { "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist }, + { "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist }, + { "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist }, + { "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2 }, + { "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2 }, + { "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index }, + { "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index }, + { "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, + { "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, + { "flh", rv_codec_i, rv_fmt_frd_offset_rs1 }, + { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1 }, + { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1 }, + { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1 }, + { "fli.s", rv_codec_fli, rv_fmt_fli }, + { "fli.d", rv_codec_fli, rv_fmt_fli }, + { "fli.q", rv_codec_fli, rv_fmt_fli }, + { "fli.h", rv_codec_fli, rv_fmt_fli }, + { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2 }, + { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, + { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, + { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1 }, + { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2 }, + { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1 }, + { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2 }, + { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2 }, + { "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, + { "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, + { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, + { "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, + { "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm }, + { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, + { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, + { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, + { "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, + { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, + { "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, + { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, + { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, + { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, + { "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, + { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "mop.r.0", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.1", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.2", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.3", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.4", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.5", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.6", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.7", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.8", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.9", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.10", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.11", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.12", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.13", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.14", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.15", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.16", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.17", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.18", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.19", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.20", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.21", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.22", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.23", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.24", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.25", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.26", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.27", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.28", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.29", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.30", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.r.31", rv_codec_r, rv_fmt_rd_rs1 }, + { "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2 }, + { "c.mop.1", rv_codec_ci_none, rv_fmt_none }, + { "c.mop.3", rv_codec_ci_none, rv_fmt_none }, + { "c.mop.5", rv_codec_ci_none, rv_fmt_none }, + { "c.mop.7", rv_codec_ci_none, rv_fmt_none }, + { "c.mop.9", rv_codec_ci_none, rv_fmt_none }, + { "c.mop.11", rv_codec_ci_none, rv_fmt_none }, + { "c.mop.13", rv_codec_ci_none, rv_fmt_none }, + { "c.mop.15", rv_codec_ci_none, rv_fmt_none }, + { "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "wrs.sto", rv_codec_none, rv_fmt_none }, + { "wrs.nto", rv_codec_none, rv_fmt_none }, + { "lpad", rv_codec_lp, rv_fmt_imm }, + { "sspush", rv_codec_r, rv_fmt_rs2 }, + { "sspopchk", rv_codec_r, rv_fmt_rs1 }, + { "ssrdp", rv_codec_r, rv_fmt_rd }, + { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, + { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, NULL, rv_op_sspush, rv_op_sspush, 0 }, { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, NULL, rv_op_sspopchk, rv_op_sspopchk, 0 }, - { "cbo.inval", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "cbo.clean", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "cbo.flush", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "cbo.zero", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 }, - { "mnret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + { "cbo.inval", rv_codec_r, rv_fmt_rs1 }, + { "cbo.clean", rv_codec_r, rv_fmt_rs1 }, + { "cbo.flush", rv_codec_r, rv_fmt_rs1 }, + { "cbo.zero", rv_codec_r, rv_fmt_rs1 }, + { "mnret", rv_codec_none, rv_fmt_none }, }; /* CSR names */ From 81305446b4eb847f0613dbae3510e08d5fb56929 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:01 -0700 Subject: [PATCH 759/860] disas/riscv: Drop useless const in structures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Individual fields should not be const, only full structures. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-12-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/disas/riscv.h b/disas/riscv.h index eabe193b4f..3325b7b4ef 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -174,19 +174,19 @@ typedef enum { /* structures */ typedef struct { - const int op; + int op; const rvc_constraint *constraints; } rv_comp_data; typedef struct { - const char * const name; - const rv_codec codec; - const char * const format; + const char *name; + rv_codec codec; + const char *format; const rv_comp_data *pseudo; - const short decomp_rv32; - const short decomp_rv64; - const short decomp_rv128; - const short decomp_data; + short decomp_rv32; + short decomp_rv64; + short decomp_rv128; + short decomp_data; } rv_opcode_data; typedef struct { From 4a5a8434bb8d71cbed6e71a7355e59eb514a97fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:02 -0700 Subject: [PATCH 760/860] disas/riscv: Drop codec from rv_decode This is mostly write-only, only used in one place; other updates are ignored. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-13-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 7 +++---- disas/riscv.h | 1 - 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index ec40abb618..9813d75feb 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -4603,8 +4603,9 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa, { rv_inst inst = dec->inst; - dec->codec = op->codec; - switch (dec->codec) { + switch (op->codec) { + case rv_codec_illegal: + break; case rv_codec_none: dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; dec->imm = 0; @@ -5369,7 +5370,6 @@ static const rv_opcode_data *decode_inst_lift_pseudo(rv_decode *dec, if (check_constraints(dec, comp_data->constraints)) { dec->op = comp_data->op; op = &dec->opcode_data[dec->op]; - dec->codec = op->codec; break; } comp_data++; @@ -5407,7 +5407,6 @@ static const rv_opcode_data *decode_inst_decompress(rv_decode *dec, rv_isa isa, dec->op = decomp_op; } op = &dec->opcode_data[decomp_op]; - dec->codec = op->codec; } return op; } diff --git a/disas/riscv.h b/disas/riscv.h index 3325b7b4ef..31bb0db86c 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -197,7 +197,6 @@ typedef struct { int32_t imm; int32_t imm1; uint16_t op; - uint8_t codec; uint8_t rd; uint8_t rs1; uint8_t rs2; From ae8f42f440265cc17c902f56f1ca94db86e6411f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:03 -0700 Subject: [PATCH 761/860] disas/riscv: Handle c.addi4spn imm != 0 during decode Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-14-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 9813d75feb..8a957e6a1e 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1885,7 +1885,7 @@ static const rv_opcode_data rvi_opcode_data[] = { { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1 }, { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1 }, { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, - rv_op_addi, rv_op_addi, rvcd_imm_nz }, + rv_op_addi, rv_op_addi }, { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 }, { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, @@ -2952,7 +2952,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 0) & 0b11) { case 0: switch ((inst >> 13) & 0b111) { - case 0: op = rv_op_c_addi4spn; break; + case 0: + if ((inst >> 5) & 0xff) { + op = rv_op_c_addi4spn; + } + break; case 1: if (isa == rv128) { op = rv_op_c_lq; From 87f162e424d2622732b82749594f57271af7b3af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:04 -0700 Subject: [PATCH 762/860] disas/riscv: Simplify c.addi While c.addi with imm == 0 and rd == 0 is c.nop, other c.addi with imm == 0 and rd != 0 are not illegal, but are reserved as HINTs. Go ahead and disassemble as c.addi rather than falling back to invalid. Further, there's no reason to differentiate c.nop, since we have c.addi -> addi -> nop vs c.nop -> addi -> nop via decompress and pseudo translation steps. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-15-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 8a957e6a1e..7978ffb378 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -263,7 +263,6 @@ typedef enum { rv_op_c_fsd = 231, rv_op_c_sw = 232, rv_op_c_fsw = 233, - rv_op_c_nop = 234, rv_op_c_addi = 235, rv_op_c_jal = 236, rv_op_c_li = 237, @@ -1896,10 +1895,9 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw }, { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, - { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, - rv_op_addi }, + { }, { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, - rv_op_addi, rvcd_imm_nz }, + rv_op_addi }, { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, @@ -3010,10 +3008,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: switch ((inst >> 13) & 0b111) { case 0: - switch ((inst >> 2) & 0b11111111111) { - case 0: op = rv_op_c_nop; break; - default: op = rv_op_c_addi; break; - } + op = rv_op_c_addi; /* or unspecified HINT */ break; case 1: if (isa == rv32) { From 3421486be8e9420eb357b878d867f47c9647c1c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:05 -0700 Subject: [PATCH 763/860] disas/riscv: Handle c.lui imm != 0 during decode Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-16-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 7978ffb378..d6fbb3bca6 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1902,9 +1902,9 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi }, { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, - rv_op_addi, rv_op_addi, rvcd_imm_nz }, + rv_op_addi, rv_op_addi }, { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, NULL, rv_op_lui, rv_op_lui, - rv_op_lui, rvcd_imm_nz }, + rv_op_lui }, { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli, rvcd_imm_nz }, { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, @@ -3032,9 +3032,11 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; } } - switch ((inst >> 7) & 0b11111) { - case 2: op = rv_op_c_addi16sp; break; - default: op = rv_op_c_lui; break; + if (inst & ((1 << 12) | (0x1f << 2))) { + switch ((inst >> 7) & 0b11111) { + case 2: op = rv_op_c_addi16sp; break; + default: op = rv_op_c_lui; break; + } } break; case 4: From dc2fe85523d059d34c7f4ba35aae57bf7218b396 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:06 -0700 Subject: [PATCH 764/860] disas/riscv: Handle c.{srli,srai} imm during decode Zero shift immediate to c.srli and c.srai are not illegal, but are reserved as HINTs. Go ahead and disassemble as shifts rather than falling back to invalid. On the other hand, shift immediate >= 32 with RV32 is reserved for custom extensions, and we need to reject those early so that the extension disassemblers get a look in. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-17-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index d6fbb3bca6..a8fbefb9bd 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1906,9 +1906,9 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, NULL, rv_op_lui, rv_op_lui, rv_op_lui }, { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, - rv_op_srli, rv_op_srli, rvcd_imm_nz }, + rv_op_srli, rv_op_srli }, { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, - rv_op_srai, rv_op_srai, rvcd_imm_nz }, + rv_op_srai, rv_op_srai }, { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi }, { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, @@ -3042,10 +3042,16 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 4: switch ((inst >> 10) & 0b11) { case 0: - op = rv_op_c_srli; + /* For rv32, shamt[5]=1 is designated for custom extensions. */ + if (isa != rv32 || (inst & 0x1000) == 0) { + op = rv_op_c_srli; /* or unspecified HINT */ + } break; case 1: - op = rv_op_c_srai; + /* For rv32, shamt[5]=1 is designated for custom extensions. */ + if (isa != rv32 || (inst & 0x1000) == 0) { + op = rv_op_c_srai; /* or unspecified HINT */ + } break; case 2: op = rv_op_c_andi; break; case 3: From 4de89e9a82fcf8e87a53895bbc01bc0434c92793 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:07 -0700 Subject: [PATCH 765/860] disas/riscv: Handle c.slli imm != 0 during decode Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-18-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index a8fbefb9bd..77347b557d 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1930,7 +1930,7 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne }, { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, - rv_op_slli, rv_op_slli, rvcd_imm_nz }, + rv_op_slli, rv_op_slli }, { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld }, { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, @@ -3085,7 +3085,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 2: switch ((inst >> 13) & 0b111) { case 0: - op = rv_op_c_slli; + if (isa != rv32 || (inst & 0x1000) == 0) { + op = rv_op_c_slli; /* or unspecified HINT */ + } break; case 1: if (isa == rv128) { From 70c0e443432dda01d4e603961e0baee54e668f4d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:08 -0700 Subject: [PATCH 766/860] disas/riscv: Do not recognize c.{addw, subw} with rv32 These code points are reserved with RV32. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-19-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 77347b557d..ddc2f73eae 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -3060,8 +3060,16 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) case 1: op = rv_op_c_xor; break; case 2: op = rv_op_c_or; break; case 3: op = rv_op_c_and; break; - case 4: op = rv_op_c_subw; break; - case 5: op = rv_op_c_addw; break; + case 4: + if (isa != rv32) { + op = rv_op_c_subw; + } + break; + case 5: + if (isa != rv32) { + op = rv_op_c_addw; + } + break; case 6: op = rv_op_c_mul; break; case 7: switch ((inst >> 2) & 0b111) { From af216549cb0651404fbe7eebc9a0e377faeb57f1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:09 -0700 Subject: [PATCH 767/860] disas/riscv: Remove rvcd_imm_nz Remove rvcd_imm_nz and rv_opcode_data.decomp_data as unused. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-20-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 7 +------ disas/riscv.h | 5 ----- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index ddc2f73eae..95957506bd 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5417,12 +5417,7 @@ static const rv_opcode_data *decode_inst_decompress(rv_decode *dec, rv_isa isa, } if (decomp_op != rv_op_illegal) { - if ((op->decomp_data & rvcd_imm_nz) && dec->imm == 0) { - dec->opcode_data = rvi_opcode_data; - dec->op = rv_op_illegal; - } else { - dec->op = decomp_op; - } + dec->op = decomp_op; op = &dec->opcode_data[decomp_op]; } return op; diff --git a/disas/riscv.h b/disas/riscv.h index 31bb0db86c..3909d73dd0 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -186,7 +186,6 @@ typedef struct { short decomp_rv32; short decomp_rv64; short decomp_rv128; - short decomp_data; } rv_opcode_data; typedef struct { @@ -217,10 +216,6 @@ enum { rv_op_illegal = 0 }; -enum { - rvcd_imm_nz = 0x1 -}; - /* instruction formats */ #define rv_fmt_none "O\t" From ebb571dc8066ba66137f6a7657d95a43e7728563 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:10 -0700 Subject: [PATCH 768/860] disas/riscv: Unify decomp_rv{32,64,128} We now distinguish compressed opcodes by isa during decode. Therefore we don't need 3 copies of decomp_*. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-21-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 155 ++++++++++++++++---------------------------------- disas/riscv.h | 4 +- 2 files changed, 51 insertions(+), 108 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 95957506bd..2e891f509e 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1883,91 +1883,52 @@ static const rv_opcode_data rvi_opcode_data[] = { { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1 }, { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1 }, - { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, - rv_op_addi, rv_op_addi }, - { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, - rv_op_fld, 0 }, - { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, - rv_op_lw }, - { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 }, - { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, - rv_op_fsd, 0 }, - { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, - rv_op_sw }, - { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 }, + { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, + { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld }, + { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw }, + { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw }, + { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd }, + { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw }, + { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw }, { }, - { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, - rv_op_addi }, - { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 }, - { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, - rv_op_addi }, - { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, - rv_op_addi, rv_op_addi }, - { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, NULL, rv_op_lui, rv_op_lui, - rv_op_lui }, - { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, - rv_op_srli, rv_op_srli }, - { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, - rv_op_srai, rv_op_srai }, - { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, - rv_op_andi, rv_op_andi }, - { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, - rv_op_sub }, - { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, - rv_op_xor }, - { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, - rv_op_or }, - { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, - rv_op_and }, - { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, - rv_op_subw }, - { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, - rv_op_addw }, - { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, - rv_op_jal }, - { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, - rv_op_beq }, - { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, - rv_op_bne }, - { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, - rv_op_slli, rv_op_slli }, - { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, - rv_op_fld, rv_op_fld }, - { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, - rv_op_lw, rv_op_lw }, - { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, - 0 }, - { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, - rv_op_jalr, rv_op_jalr }, - { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, - rv_op_addi }, - { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, - rv_op_ebreak, rv_op_ebreak }, - { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, - rv_op_jalr, rv_op_jalr }, - { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, - rv_op_add }, - { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, - rv_op_fsd, rv_op_fsd }, - { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, - rv_op_sw, rv_op_sw }, - { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, - 0 }, - { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, - rv_op_ld }, - { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, - rv_op_sd }, - { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, - rv_op_addiw }, - { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, - rv_op_ld }, - { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, - rv_op_sd }, - { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, - { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq }, - { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq }, - { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, - rv_op_sq }, + { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, + { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal }, + { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, + { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, + { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, NULL, rv_op_lui }, + { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli }, + { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai }, + { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi }, + { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub }, + { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor }, + { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or }, + { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and }, + { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw }, + { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw }, + { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal }, + { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq }, + { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne }, + { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli }, + { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld }, + { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw }, + { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw }, + { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr }, + { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi }, + { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak }, + { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr }, + { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add }, + { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd }, + { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw }, + { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw }, + { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, rv_op_ld }, + { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, rv_op_sd }, + { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addiw }, + { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, rv_op_ld }, + { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sd }, + { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, rv_op_lq }, + { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, rv_op_sq }, + { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lq }, + { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sq }, { "nop", rv_codec_i, rv_fmt_none }, { "mv", rv_codec_i, rv_fmt_rd_rs1 }, { "not", rv_codec_i, rv_fmt_rd_rs1 }, @@ -2649,10 +2610,8 @@ static const rv_opcode_data rvi_opcode_data[] = { { "ssrdp", rv_codec_r, rv_fmt_rd }, { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, NULL, rv_op_sspush, - rv_op_sspush, 0 }, - { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, NULL, rv_op_sspopchk, - rv_op_sspopchk, 0 }, + { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, NULL, rv_op_sspush }, + { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, NULL, rv_op_sspopchk }, { "cbo.inval", rv_codec_r, rv_fmt_rs1 }, { "cbo.clean", rv_codec_r, rv_fmt_rs1 }, { "cbo.flush", rv_codec_r, rv_fmt_rs1 }, @@ -5397,24 +5356,10 @@ static const rv_opcode_data *decode_inst_lift_pseudo(rv_decode *dec, /* decompress instruction */ -static const rv_opcode_data *decode_inst_decompress(rv_decode *dec, rv_isa isa, +static const rv_opcode_data *decode_inst_decompress(rv_decode *dec, const rv_opcode_data *op) { - int decomp_op; - - switch (isa) { - case rv32: - decomp_op = op->decomp_rv32; - break; - case rv64: - decomp_op = op->decomp_rv64; - break; - case rv128: - decomp_op = op->decomp_rv128; - break; - default: - g_assert_not_reached(); - } + int decomp_op = op->decomp; if (decomp_op != rv_op_illegal) { dec->op = decomp_op; @@ -5473,7 +5418,7 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, op = &dec.opcode_data[dec.op]; decode_inst_operands(&dec, isa, op); - op = decode_inst_decompress(&dec, isa, op); + op = decode_inst_decompress(&dec, op); op = decode_inst_lift_pseudo(&dec, op); return format_inst(24, &dec, op); } diff --git a/disas/riscv.h b/disas/riscv.h index 3909d73dd0..900363c603 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -183,9 +183,7 @@ typedef struct { rv_codec codec; const char *format; const rv_comp_data *pseudo; - short decomp_rv32; - short decomp_rv64; - short decomp_rv128; + rv_opcode decomp; } rv_opcode_data; typedef struct { From 7376e4d7d29c7c4bf4ad552624502c92e2deac06 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:11 -0700 Subject: [PATCH 769/860] disas/riscv: Drop always true branch pseudos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The real insns are blt, bge, bltu, bgeu. Do not include pseudos that unconditionally swap operands. That's fine for an assembler but not a disassembler. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-22-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 2e891f509e..bde1cc6e62 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -326,10 +326,6 @@ typedef enum { rv_op_bgez = 295, rv_op_bltz = 296, rv_op_bgtz = 297, - rv_op_ble = 298, - rv_op_bleu = 299, - rv_op_bgt = 300, - rv_op_bgtu = 301, rv_op_j = 302, rv_op_ret = 303, rv_op_jr = 304, @@ -1062,10 +1058,6 @@ static const rvc_constraint rvcc_blez[] = { rvc_rs1_eq_x0, rvc_end }; static const rvc_constraint rvcc_bgez[] = { rvc_rs2_eq_x0, rvc_end }; static const rvc_constraint rvcc_bltz[] = { rvc_rs2_eq_x0, rvc_end }; static const rvc_constraint rvcc_bgtz[] = { rvc_rs1_eq_x0, rvc_end }; -static const rvc_constraint rvcc_ble[] = { rvc_end }; -static const rvc_constraint rvcc_bleu[] = { rvc_end }; -static const rvc_constraint rvcc_bgt[] = { rvc_end }; -static const rvc_constraint rvcc_bgtu[] = { rvc_end }; static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end }; static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end }; @@ -1123,24 +1115,12 @@ static const rv_comp_data rvcp_bne[] = { static const rv_comp_data rvcp_blt[] = { { rv_op_bltz, rvcc_bltz }, { rv_op_bgtz, rvcc_bgtz }, - { rv_op_bgt, rvcc_bgt }, { rv_op_illegal, NULL } }; static const rv_comp_data rvcp_bge[] = { { rv_op_blez, rvcc_blez }, { rv_op_bgez, rvcc_bgez }, - { rv_op_ble, rvcc_ble }, - { rv_op_illegal, NULL } -}; - -static const rv_comp_data rvcp_bltu[] = { - { rv_op_bgtu, rvcc_bgtu }, - { rv_op_illegal, NULL } -}; - -static const rv_comp_data rvcp_bgeu[] = { - { rv_op_bleu, rvcc_bleu }, { rv_op_illegal, NULL } }; @@ -1665,8 +1645,8 @@ static const rv_opcode_data rvi_opcode_data[] = { { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne }, { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt }, { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge }, - { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bltu }, - { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bgeu }, + { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset }, + { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset }, { "lb", rv_codec_i, rv_fmt_rd_offset_rs1 }, { "lh", rv_codec_i, rv_fmt_rd_offset_rs1 }, { "lw", rv_codec_i, rv_fmt_rd_offset_rs1 }, @@ -1954,10 +1934,10 @@ static const rv_opcode_data rvi_opcode_data[] = { { "bgez", rv_codec_sb, rv_fmt_rs1_offset }, { "bltz", rv_codec_sb, rv_fmt_rs1_offset }, { "bgtz", rv_codec_sb, rv_fmt_rs2_offset }, - { "ble", rv_codec_sb, rv_fmt_rs2_rs1_offset }, - { "bleu", rv_codec_sb, rv_fmt_rs2_rs1_offset }, - { "bgt", rv_codec_sb, rv_fmt_rs2_rs1_offset }, - { "bgtu", rv_codec_sb, rv_fmt_rs2_rs1_offset }, + { }, + { }, + { }, + { }, { "j", rv_codec_uj, rv_fmt_offset }, { "ret", rv_codec_i, rv_fmt_none }, { "jr", rv_codec_i, rv_fmt_rs1 }, From b1d5cbf56abc5604c6848350926271e7f865f988 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:12 -0700 Subject: [PATCH 770/860] disas/riscv: Handle aliases of csrrw during decode Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-23-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index bde1cc6e62..e464b8b075 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1081,9 +1081,6 @@ static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end }; static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end }; -static const rvc_constraint rvcc_fscsr[] = { rvc_csr_eq_0x003, rvc_end }; -static const rvc_constraint rvcc_fsrm[] = { rvc_csr_eq_0x002, rvc_end }; -static const rvc_constraint rvcc_fsflags[] = { rvc_csr_eq_0x001, rvc_end }; static const rvc_constraint rvcc_fsrmi[] = { rvc_csr_eq_0x002, rvc_end }; static const rvc_constraint rvcc_fsflagsi[] = { rvc_csr_eq_0x001, rvc_end }; @@ -1166,14 +1163,6 @@ static const rv_comp_data rvcp_subw[] = { { rv_op_illegal, NULL } }; -static const rv_comp_data rvcp_csrrw[] = { - { rv_op_fscsr, rvcc_fscsr }, - { rv_op_fsrm, rvcc_fsrm }, - { rv_op_fsflags, rvcc_fsflags }, - { rv_op_illegal, NULL } -}; - - static const rv_comp_data rvcp_csrrs[] = { { rv_op_rdcycle, rvcc_rdcycle }, { rv_op_rdtime, rvcc_rdtime }, @@ -1761,7 +1750,7 @@ static const rv_opcode_data rvi_opcode_data[] = { { "sfence.vm", rv_codec_r, rv_fmt_rs1 }, { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2 }, { "wfi", rv_codec_none, rv_fmt_none }, - { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrw }, + { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs }, { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi }, @@ -4443,7 +4432,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; } break; - case 1: op = rv_op_csrrw; break; + case 1: + switch (operand_csr12(inst)) { + case 1: op = rv_op_fsflags; break; + case 2: op = rv_op_fsrm; break; + case 3: op = rv_op_fscsr; break; + default: op = rv_op_csrrw; break; + } + break; case 2: op = rv_op_csrrs; break; case 3: op = rv_op_csrrc; break; case 4: From 6bfb6410562592a7f6a1e496c99600a58b838231 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:13 -0700 Subject: [PATCH 771/860] disas/riscv: Handle aliases of csrrs during decode Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-24-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 85 +++++++++++---------------------------------------- disas/riscv.h | 7 ----- 2 files changed, 17 insertions(+), 75 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index e464b8b075..9ec79baf4d 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1063,24 +1063,6 @@ static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end }; static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; -static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, - rvc_end }; -static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, - rvc_end }; -static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, - rvc_csr_eq_0xc02, rvc_end }; -static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, - rvc_csr_eq_0xc80, rvc_end }; -static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, - rvc_end }; -static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, - rvc_csr_eq_0xc82, rvc_end }; -static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, - rvc_end }; -static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, - rvc_end }; -static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, - rvc_end }; static const rvc_constraint rvcc_fsrmi[] = { rvc_csr_eq_0x002, rvc_end }; static const rvc_constraint rvcc_fsflagsi[] = { rvc_csr_eq_0x001, rvc_end }; @@ -1163,19 +1145,6 @@ static const rv_comp_data rvcp_subw[] = { { rv_op_illegal, NULL } }; -static const rv_comp_data rvcp_csrrs[] = { - { rv_op_rdcycle, rvcc_rdcycle }, - { rv_op_rdtime, rvcc_rdtime }, - { rv_op_rdinstret, rvcc_rdinstret }, - { rv_op_rdcycleh, rvcc_rdcycleh }, - { rv_op_rdtimeh, rvcc_rdtimeh }, - { rv_op_rdinstreth, rvcc_rdinstreth }, - { rv_op_frcsr, rvcc_frcsr }, - { rv_op_frrm, rvcc_frrm }, - { rv_op_frflags, rvcc_frflags }, - { rv_op_illegal, NULL } -}; - static const rv_comp_data rvcp_csrrwi[] = { { rv_op_fsrmi, rvcc_fsrmi }, { rv_op_fsflagsi, rvcc_fsflagsi }, @@ -1751,7 +1720,7 @@ static const rv_opcode_data rvi_opcode_data[] = { { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2 }, { "wfi", rv_codec_none, rv_fmt_none }, { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, - { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs }, + { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi }, { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, @@ -4440,7 +4409,22 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) default: op = rv_op_csrrw; break; } break; - case 2: op = rv_op_csrrs; break; + case 2: + op = rv_op_csrrs; + if (operand_rs1(inst) == 0) { + switch (operand_csr12(inst)) { + case 0x001: op = rv_op_frflags; break; + case 0x002: op = rv_op_frrm; break; + case 0x003: op = rv_op_frcsr; break; + case 0xc00: op = rv_op_rdcycle; break; + case 0xc01: op = rv_op_rdtime; break; + case 0xc02: op = rv_op_rdinstret; break; + case 0xc80: op = rv_op_rdcycleh; break; + case 0xc81: op = rv_op_rdtimeh; break; + case 0xc82: op = rv_op_rdinstreth; break; + } + } + break; case 3: op = rv_op_csrrc; break; case 4: if (dec->cfg && dec->cfg->ext_zimop) { @@ -5017,41 +5001,6 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) return false; } break; - case rvc_csr_eq_0x003: - if (!(imm == 0x003)) { - return false; - } - break; - case rvc_csr_eq_0xc00: - if (!(imm == 0xc00)) { - return false; - } - break; - case rvc_csr_eq_0xc01: - if (!(imm == 0xc01)) { - return false; - } - break; - case rvc_csr_eq_0xc02: - if (!(imm == 0xc02)) { - return false; - } - break; - case rvc_csr_eq_0xc80: - if (!(imm == 0xc80)) { - return false; - } - break; - case rvc_csr_eq_0xc81: - if (!(imm == 0xc81)) { - return false; - } - break; - case rvc_csr_eq_0xc82: - if (!(imm == 0xc82)) { - return false; - } - break; default: break; } c++; diff --git a/disas/riscv.h b/disas/riscv.h index 900363c603..d8c5acd969 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -86,13 +86,6 @@ typedef enum { rvc_imm_eq_p1, rvc_csr_eq_0x001, rvc_csr_eq_0x002, - rvc_csr_eq_0x003, - rvc_csr_eq_0xc00, - rvc_csr_eq_0xc01, - rvc_csr_eq_0xc02, - rvc_csr_eq_0xc80, - rvc_csr_eq_0xc81, - rvc_csr_eq_0xc82, } rvc_constraint; typedef enum { From 9803e8277307e153dbbb1757c083167f882a9ef8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:14 -0700 Subject: [PATCH 772/860] disas/riscv: Handle aliases of csrrwi during decode Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-25-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 28 ++++++++-------------------- disas/riscv.h | 2 -- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 9ec79baf4d..d3cbfb86ac 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1063,8 +1063,6 @@ static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end }; static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; -static const rvc_constraint rvcc_fsrmi[] = { rvc_csr_eq_0x002, rvc_end }; -static const rvc_constraint rvcc_fsflagsi[] = { rvc_csr_eq_0x001, rvc_end }; /* pseudo-instruction metadata */ @@ -1145,12 +1143,6 @@ static const rv_comp_data rvcp_subw[] = { { rv_op_illegal, NULL } }; -static const rv_comp_data rvcp_csrrwi[] = { - { rv_op_fsrmi, rvcc_fsrmi }, - { rv_op_fsflagsi, rvcc_fsflagsi }, - { rv_op_illegal, NULL } -}; - static const rv_comp_data rvcp_fsgnj_s[] = { { rv_op_fmv_s, rvcc_fmv_s }, { rv_op_illegal, NULL } @@ -1722,7 +1714,7 @@ static const rv_opcode_data rvi_opcode_data[] = { { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, - { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi }, + { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, { "flw", rv_codec_i, rv_fmt_frd_offset_rs1 }, @@ -4469,7 +4461,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) } } break; - case 5: op = rv_op_csrrwi; break; + case 5: + switch (operand_csr12(inst)) { + case 1: op = rv_op_fsflagsi; break; + case 2: op = rv_op_fsrmi; break; + default: op = rv_op_csrrwi; break; + } + break; case 6: op = rv_op_csrrsi; break; case 7: op = rv_op_csrrci; break; } @@ -4991,16 +4989,6 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) return false; } break; - case rvc_csr_eq_0x001: - if (!(imm == 0x001)) { - return false; - } - break; - case rvc_csr_eq_0x002: - if (!(imm == 0x002)) { - return false; - } - break; default: break; } c++; diff --git a/disas/riscv.h b/disas/riscv.h index d8c5acd969..3bbcbf10fb 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -84,8 +84,6 @@ typedef enum { rvc_imm_eq_zero, rvc_imm_eq_n1, rvc_imm_eq_p1, - rvc_csr_eq_0x001, - rvc_csr_eq_0x002, } rvc_constraint; typedef enum { From 4abc45a78ff3cb6dbb2d74d8d651e2325c731c28 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:15 -0700 Subject: [PATCH 773/860] disas/riscv: Break pseudo loop for jal and jalr These were clearly intending to simplify jal ra, foo to jal foo and similarly for jalr, but the pseudo expansion looped back to the original jal/jalr with the full format. Add new opcode expansions dropping the implied ra. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-26-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index d3cbfb86ac..fe503b6cae 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -326,6 +326,8 @@ typedef enum { rv_op_bgez = 295, rv_op_bltz = 296, rv_op_bgtz = 297, + rv_op_jal_ra = 298, + rv_op_jalr_ra = 299, rv_op_j = 302, rv_op_ret = 303, rv_op_jr = 304, @@ -1029,9 +1031,9 @@ static const char rv_fli_name_const[32][9] = /* pseudo-instruction constraints */ -static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end }; -static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, - rvc_end }; +static const rvc_constraint rvcc_jal_ra[] = { rvc_rd_eq_ra, rvc_end }; +static const rvc_constraint rvcc_jalr_ra[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, + rvc_end }; static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end }; @@ -1068,14 +1070,14 @@ static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, static const rv_comp_data rvcp_jal[] = { { rv_op_j, rvcc_j }, - { rv_op_jal, rvcc_jal }, + { rv_op_jal_ra, rvcc_jal_ra }, { rv_op_illegal, NULL } }; static const rv_comp_data rvcp_jalr[] = { { rv_op_ret, rvcc_ret }, { rv_op_jr, rvcc_jr }, - { rv_op_jalr, rvcc_jalr }, + { rv_op_jalr_ra, rvcc_jalr_ra }, { rv_op_illegal, NULL } }; @@ -1884,8 +1886,8 @@ static const rv_opcode_data rvi_opcode_data[] = { { "bgez", rv_codec_sb, rv_fmt_rs1_offset }, { "bltz", rv_codec_sb, rv_fmt_rs1_offset }, { "bgtz", rv_codec_sb, rv_fmt_rs2_offset }, - { }, - { }, + { "jal", rv_codec_none, rv_fmt_offset }, /* rv_op_jal_ra */ + { "jalr", rv_codec_none, rv_fmt_rs1 }, /* rv_op_jalr_ra */ { }, { }, { "j", rv_codec_uj, rv_fmt_offset }, From 6b1230862c221986c1548c423db4b48967de09ea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:16 -0700 Subject: [PATCH 774/860] disas/riscv: Allow decode_inst_lift_pseudo to loop Allow pseudo expansion to proceed in multiple steps. Assert that we don't have simple loops. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-27-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index fe503b6cae..b8e5b124a5 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5259,9 +5259,9 @@ static const rv_opcode_data *decode_inst_lift_pseudo(rv_decode *dec, if (comp_data) { while (comp_data->constraints) { if (check_constraints(dec, comp_data->constraints)) { - dec->op = comp_data->op; - op = &dec->opcode_data[dec->op]; - break; + const rv_opcode_data *new_op = &dec->opcode_data[comp_data->op]; + assert(new_op != op); + return decode_inst_lift_pseudo(dec, new_op); } comp_data++; } From 34adc6c2dc4c8771ce80b04b87e6b1c7fd96c457 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:17 -0700 Subject: [PATCH 775/860] disas/riscv: Handle decompression via decode_inst_lift_pseudo Introduce a DECOMP macro that expands an rv_comp_data that always succeeds. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-28-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 113 +++++++++++++++++++++++--------------------------- disas/riscv.h | 1 - 2 files changed, 51 insertions(+), 63 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index b8e5b124a5..a359badab5 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1065,6 +1065,7 @@ static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end }; static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_true[] = { rvc_end }; /* pseudo-instruction metadata */ @@ -1190,6 +1191,9 @@ static const rv_comp_data rvcp_fsgnjx_q[] = { { rv_op_illegal, NULL } }; +/* Convert compressed insns into normal insns via pseudo expansion. */ +#define DECOMP(X) &(const rv_comp_data){ X, rvcc_true } + /* operand extractors */ static uint32_t operand_rd(rv_inst inst) @@ -1815,52 +1819,52 @@ static const rv_opcode_data rvi_opcode_data[] = { { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1 }, { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1 }, - { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, - { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld }, - { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw }, - { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw }, - { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd }, - { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw }, - { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw }, + { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, + { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, DECOMP(rv_op_fld) }, + { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lw) }, + { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, DECOMP(rv_op_flw) }, + { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsd) }, + { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sw) }, + { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsw) }, { }, - { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, - { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal }, - { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, - { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi }, - { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, NULL, rv_op_lui }, - { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli }, - { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai }, - { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi }, - { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub }, - { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor }, - { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or }, - { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and }, - { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw }, - { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw }, - { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal }, - { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq }, - { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne }, - { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli }, - { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld }, - { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw }, - { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw }, - { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr }, - { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi }, - { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak }, - { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr }, - { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add }, - { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd }, - { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw }, - { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw }, - { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, rv_op_ld }, - { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, rv_op_sd }, - { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addiw }, - { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, rv_op_ld }, - { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sd }, - { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, rv_op_lq }, - { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, rv_op_sq }, - { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lq }, - { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sq }, + { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, + { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, DECOMP(rv_op_jal) }, + { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, + { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, + { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, DECOMP(rv_op_lui) }, + { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, DECOMP(rv_op_srli) }, + { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, DECOMP(rv_op_srai) }, + { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, DECOMP(rv_op_andi) }, + { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_sub) }, + { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_xor) }, + { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_or) }, + { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_and) }, + { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_subw) }, + { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_addw) }, + { "c.j", rv_codec_cj, rv_fmt_rd_offset, DECOMP(rv_op_jal) }, + { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, DECOMP(rv_op_beq) }, + { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, DECOMP(rv_op_bne) }, + { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, DECOMP(rv_op_slli) }, + { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, DECOMP(rv_op_fld) }, + { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lw) }, + { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, DECOMP(rv_op_flw) }, + { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, DECOMP(rv_op_jalr) }, + { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_addi) }, + { "c.ebreak", rv_codec_ci_none, rv_fmt_none, DECOMP(rv_op_ebreak) }, + { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, DECOMP(rv_op_jalr) }, + { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_add) }, + { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsd) }, + { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sw) }, + { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsw) }, + { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, DECOMP(rv_op_ld) }, + { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sd) }, + { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addiw) }, + { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, DECOMP(rv_op_ld) }, + { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sd) }, + { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lq) }, + { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sq) }, + { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lq) }, + { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sq) }, { "nop", rv_codec_i, rv_fmt_none }, { "mv", rv_codec_i, rv_fmt_rd_rs1 }, { "not", rv_codec_i, rv_fmt_rd_rs1 }, @@ -2542,8 +2546,8 @@ static const rv_opcode_data rvi_opcode_data[] = { { "ssrdp", rv_codec_r, rv_fmt_rd }, { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, NULL, rv_op_sspush }, - { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, NULL, rv_op_sspopchk }, + { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, DECOMP(rv_op_sspush) }, + { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, DECOMP(rv_op_sspopchk) }, { "cbo.inval", rv_codec_r, rv_fmt_rs1 }, { "cbo.clean", rv_codec_r, rv_fmt_rs1 }, { "cbo.flush", rv_codec_r, rv_fmt_rs1 }, @@ -5269,20 +5273,6 @@ static const rv_opcode_data *decode_inst_lift_pseudo(rv_decode *dec, return op; } -/* decompress instruction */ - -static const rv_opcode_data *decode_inst_decompress(rv_decode *dec, - const rv_opcode_data *op) -{ - int decomp_op = op->decomp; - - if (decomp_op != rv_op_illegal) { - dec->op = decomp_op; - op = &dec->opcode_data[decomp_op]; - } - return op; -} - /* disassemble instruction */ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, @@ -5333,7 +5323,6 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, op = &dec.opcode_data[dec.op]; decode_inst_operands(&dec, isa, op); - op = decode_inst_decompress(&dec, op); op = decode_inst_lift_pseudo(&dec, op); return format_inst(24, &dec, op); } diff --git a/disas/riscv.h b/disas/riscv.h index 3bbcbf10fb..ea7141d3b5 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -174,7 +174,6 @@ typedef struct { rv_codec codec; const char *format; const rv_comp_data *pseudo; - rv_opcode decomp; } rv_opcode_data; typedef struct { From ed3a538849dab7e945a2e76df4bcd6f48898d618 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:18 -0700 Subject: [PATCH 776/860] disas/riscv: Drop format from DECOMP insns The format for these will never be used -- we will use the format from the decompressed insn. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-29-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 94 +++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index a359badab5..e0b3d22031 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1819,52 +1819,52 @@ static const rv_opcode_data rvi_opcode_data[] = { { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1 }, { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1 }, - { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, - { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, DECOMP(rv_op_fld) }, - { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lw) }, - { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, DECOMP(rv_op_flw) }, - { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsd) }, - { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sw) }, - { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsw) }, + { "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(rv_op_addi) }, + { "c.fld", rv_codec_cl_ld, NULL, DECOMP(rv_op_fld) }, + { "c.lw", rv_codec_cl_lw, NULL, DECOMP(rv_op_lw) }, + { "c.flw", rv_codec_cl_lw, NULL, DECOMP(rv_op_flw) }, + { "c.fsd", rv_codec_cs_sd, NULL, DECOMP(rv_op_fsd) }, + { "c.sw", rv_codec_cs_sw, NULL, DECOMP(rv_op_sw) }, + { "c.fsw", rv_codec_cs_sw, NULL, DECOMP(rv_op_fsw) }, { }, - { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, - { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, DECOMP(rv_op_jal) }, - { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, - { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addi) }, - { "c.lui", rv_codec_ci_lui, rv_fmt_rd_uimm, DECOMP(rv_op_lui) }, - { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, DECOMP(rv_op_srli) }, - { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, DECOMP(rv_op_srai) }, - { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, DECOMP(rv_op_andi) }, - { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_sub) }, - { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_xor) }, - { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_or) }, - { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_and) }, - { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_subw) }, - { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_addw) }, - { "c.j", rv_codec_cj, rv_fmt_rd_offset, DECOMP(rv_op_jal) }, - { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, DECOMP(rv_op_beq) }, - { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, DECOMP(rv_op_bne) }, - { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, DECOMP(rv_op_slli) }, - { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, DECOMP(rv_op_fld) }, - { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lw) }, - { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, DECOMP(rv_op_flw) }, - { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, DECOMP(rv_op_jalr) }, - { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_addi) }, - { "c.ebreak", rv_codec_ci_none, rv_fmt_none, DECOMP(rv_op_ebreak) }, - { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, DECOMP(rv_op_jalr) }, - { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, DECOMP(rv_op_add) }, - { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsd) }, - { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sw) }, - { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, DECOMP(rv_op_fsw) }, - { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, DECOMP(rv_op_ld) }, - { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sd) }, - { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, DECOMP(rv_op_addiw) }, - { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, DECOMP(rv_op_ld) }, - { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sd) }, - { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lq) }, - { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sq) }, - { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, DECOMP(rv_op_lq) }, - { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, DECOMP(rv_op_sq) }, + { "c.addi", rv_codec_ci, NULL, DECOMP(rv_op_addi) }, + { "c.jal", rv_codec_cj_jal, NULL, DECOMP(rv_op_jal) }, + { "c.li", rv_codec_ci_li, NULL, DECOMP(rv_op_addi) }, + { "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(rv_op_addi) }, + { "c.lui", rv_codec_ci_lui, NULL, DECOMP(rv_op_lui) }, + { "c.srli", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srli) }, + { "c.srai", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srai) }, + { "c.andi", rv_codec_cb_imm, NULL, DECOMP(rv_op_andi) }, + { "c.sub", rv_codec_cs, NULL, DECOMP(rv_op_sub) }, + { "c.xor", rv_codec_cs, NULL, DECOMP(rv_op_xor) }, + { "c.or", rv_codec_cs, NULL, DECOMP(rv_op_or) }, + { "c.and", rv_codec_cs, NULL, DECOMP(rv_op_and) }, + { "c.subw", rv_codec_cs, NULL, DECOMP(rv_op_subw) }, + { "c.addw", rv_codec_cs, NULL, DECOMP(rv_op_addw) }, + { "c.j", rv_codec_cj, NULL, DECOMP(rv_op_jal) }, + { "c.beqz", rv_codec_cb, NULL, DECOMP(rv_op_beq) }, + { "c.bnez", rv_codec_cb, NULL, DECOMP(rv_op_bne) }, + { "c.slli", rv_codec_ci_sh6, NULL, DECOMP(rv_op_slli) }, + { "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_fld) }, + { "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_lw) }, + { "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_flw) }, + { "c.jr", rv_codec_cr_jr, NULL, DECOMP(rv_op_jalr) }, + { "c.mv", rv_codec_cr_mv, NULL, DECOMP(rv_op_addi) }, + { "c.ebreak", rv_codec_ci_none, NULL, DECOMP(rv_op_ebreak) }, + { "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(rv_op_jalr) }, + { "c.add", rv_codec_cr, NULL, DECOMP(rv_op_add) }, + { "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_fsd) }, + { "c.swsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_sw) }, + { "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_fsw) }, + { "c.ld", rv_codec_cl_ld, NULL, DECOMP(rv_op_ld) }, + { "c.sd", rv_codec_cs_sd, NULL, DECOMP(rv_op_sd) }, + { "c.addiw", rv_codec_ci, NULL, DECOMP(rv_op_addiw) }, + { "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_ld) }, + { "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_sd) }, + { "c.lq", rv_codec_cl_lq, NULL, DECOMP(rv_op_lq) }, + { "c.sq", rv_codec_cs_sq, NULL, DECOMP(rv_op_sq) }, + { "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(rv_op_lq) }, + { "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(rv_op_sq) }, { "nop", rv_codec_i, rv_fmt_none }, { "mv", rv_codec_i, rv_fmt_rd_rs1 }, { "not", rv_codec_i, rv_fmt_rd_rs1 }, @@ -2546,8 +2546,8 @@ static const rv_opcode_data rvi_opcode_data[] = { { "ssrdp", rv_codec_r, rv_fmt_rd }, { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "c.sspush", rv_codec_cmop_ss, rv_fmt_rs2, DECOMP(rv_op_sspush) }, - { "c.sspopchk", rv_codec_cmop_ss, rv_fmt_rs1, DECOMP(rv_op_sspopchk) }, + { "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspush) }, + { "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspopchk) }, { "cbo.inval", rv_codec_r, rv_fmt_rs1 }, { "cbo.clean", rv_codec_r, rv_fmt_rs1 }, { "cbo.flush", rv_codec_r, rv_fmt_rs1 }, From 0a2382f0867a1f66b3ae79d974426aa939eaf129 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:19 -0700 Subject: [PATCH 777/860] disas/riscv: Tidy rv_comp_data terminators Use { } instead of two zeros. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-30-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index e0b3d22031..f3c9213fd1 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1072,123 +1072,123 @@ static const rvc_constraint rvcc_true[] = { rvc_end }; static const rv_comp_data rvcp_jal[] = { { rv_op_j, rvcc_j }, { rv_op_jal_ra, rvcc_jal_ra }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_jalr[] = { { rv_op_ret, rvcc_ret }, { rv_op_jr, rvcc_jr }, { rv_op_jalr_ra, rvcc_jalr_ra }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_beq[] = { { rv_op_beqz, rvcc_beqz }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_bne[] = { { rv_op_bnez, rvcc_bnez }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_blt[] = { { rv_op_bltz, rvcc_bltz }, { rv_op_bgtz, rvcc_bgtz }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_bge[] = { { rv_op_blez, rvcc_blez }, { rv_op_bgez, rvcc_bgez }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_addi[] = { { rv_op_nop, rvcc_nop }, { rv_op_mv, rvcc_mv }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_sltiu[] = { { rv_op_seqz, rvcc_seqz }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_xori[] = { { rv_op_not, rvcc_not }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_sub[] = { { rv_op_neg, rvcc_neg }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_slt[] = { { rv_op_sltz, rvcc_sltz }, { rv_op_sgtz, rvcc_sgtz }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_sltu[] = { { rv_op_snez, rvcc_snez }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_addiw[] = { { rv_op_sext_w, rvcc_sext_w }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_subw[] = { { rv_op_negw, rvcc_negw }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnj_s[] = { { rv_op_fmv_s, rvcc_fmv_s }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnjn_s[] = { { rv_op_fneg_s, rvcc_fneg_s }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnjx_s[] = { { rv_op_fabs_s, rvcc_fabs_s }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnj_d[] = { { rv_op_fmv_d, rvcc_fmv_d }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnjn_d[] = { { rv_op_fneg_d, rvcc_fneg_d }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnjx_d[] = { { rv_op_fabs_d, rvcc_fabs_d }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnj_q[] = { { rv_op_fmv_q, rvcc_fmv_q }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnjn_q[] = { { rv_op_fneg_q, rvcc_fneg_q }, - { rv_op_illegal, NULL } + { }, }; static const rv_comp_data rvcp_fsgnjx_q[] = { { rv_op_fabs_q, rvcc_fabs_q }, - { rv_op_illegal, NULL } + { }, }; /* Convert compressed insns into normal insns via pseudo expansion. */ From 583b84e18f4e793aade850315013f60ed8ae2d84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:20 -0700 Subject: [PATCH 778/860] disas/riscv: Chain "ret" pseudo off "jr" pseudo "jr" has already checked two conditions that apply to "ret". Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-31-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index f3c9213fd1..91f6d9e4af 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1061,8 +1061,7 @@ static const rvc_constraint rvcc_bgez[] = { rvc_rs2_eq_x0, rvc_end }; static const rvc_constraint rvcc_bltz[] = { rvc_rs2_eq_x0, rvc_end }; static const rvc_constraint rvcc_bgtz[] = { rvc_rs1_eq_x0, rvc_end }; static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end }; -static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, - rvc_end }; +static const rvc_constraint rvcc_ret[] = { rvc_rs1_eq_ra, rvc_end }; static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_true[] = { rvc_end }; @@ -1076,12 +1075,16 @@ static const rv_comp_data rvcp_jal[] = { }; static const rv_comp_data rvcp_jalr[] = { - { rv_op_ret, rvcc_ret }, { rv_op_jr, rvcc_jr }, { rv_op_jalr_ra, rvcc_jalr_ra }, { }, }; +static const rv_comp_data rvcp_jr[] = { + { rv_op_ret, rvcc_ret }, + { }, +}; + static const rv_comp_data rvcp_beq[] = { { rv_op_beqz, rvcc_beqz }, { }, @@ -1896,7 +1899,7 @@ static const rv_opcode_data rvi_opcode_data[] = { { }, { "j", rv_codec_uj, rv_fmt_offset }, { "ret", rv_codec_i, rv_fmt_none }, - { "jr", rv_codec_i, rv_fmt_rs1 }, + { "jr", rv_codec_i, rv_fmt_rs1, rvcp_jr }, { "rdcycle", rv_codec_i_csr, rv_fmt_rd }, { "rdtime", rv_codec_i_csr, rv_fmt_rd }, { "rdinstret", rv_codec_i_csr, rv_fmt_rd }, From 78b2b441a6cc364d63874e0d35391345b36038b8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:21 -0700 Subject: [PATCH 779/860] disas/riscv: Chain "nop" pseudo off "mv" pseudo "mv" has already checked a condition that applies to "nop". Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-32-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 91f6d9e4af..7fa76b2d15 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1035,7 +1035,7 @@ static const rvc_constraint rvcc_jal_ra[] = { rvc_rd_eq_ra, rvc_end }; static const rvc_constraint rvcc_jalr_ra[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, - rvc_imm_eq_zero, rvc_end }; + rvc_end }; static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end }; static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end }; static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end }; @@ -1108,11 +1108,15 @@ static const rv_comp_data rvcp_bge[] = { }; static const rv_comp_data rvcp_addi[] = { - { rv_op_nop, rvcc_nop }, { rv_op_mv, rvcc_mv }, { }, }; +static const rv_comp_data rvcp_mv[] = { + { rv_op_nop, rvcc_nop }, + { }, +}; + static const rv_comp_data rvcp_sltiu[] = { { rv_op_seqz, rvcc_seqz }, { }, @@ -1869,7 +1873,7 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(rv_op_lq) }, { "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(rv_op_sq) }, { "nop", rv_codec_i, rv_fmt_none }, - { "mv", rv_codec_i, rv_fmt_rd_rs1 }, + { "mv", rv_codec_i, rv_fmt_rd_rs1, rvcp_mv }, { "not", rv_codec_i, rv_fmt_rd_rs1 }, { "neg", rv_codec_r, rv_fmt_rd_rs2 }, { "negw", rv_codec_r, rv_fmt_rd_rs2 }, From 73a0f25feba58f356ba65d5b888b60744331f62a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:22 -0700 Subject: [PATCH 780/860] disas/riscv: Use rv_codec_illegal for pseudos The codec is never used, since we arrive into pseudos from a decoding of another opcode. Assert that rv_codec_illegal is never decoded. Use rv_codec_none for rv_op_illegal. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-33-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 68 +++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 7fa76b2d15..dedd925077 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1599,7 +1599,7 @@ static uint32_t operand_lpl(rv_inst inst) /* instruction metadata */ static const rv_opcode_data rvi_opcode_data[] = { - { "illegal", rv_codec_illegal, rv_fmt_none }, + { "illegal", rv_codec_none, rv_fmt_none }, { "lui", rv_codec_u, rv_fmt_rd_uimm }, { "auipc", rv_codec_u, rv_fmt_rd_uoffset }, { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal }, @@ -1872,38 +1872,38 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.sq", rv_codec_cs_sq, NULL, DECOMP(rv_op_sq) }, { "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(rv_op_lq) }, { "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(rv_op_sq) }, - { "nop", rv_codec_i, rv_fmt_none }, - { "mv", rv_codec_i, rv_fmt_rd_rs1, rvcp_mv }, - { "not", rv_codec_i, rv_fmt_rd_rs1 }, - { "neg", rv_codec_r, rv_fmt_rd_rs2 }, - { "negw", rv_codec_r, rv_fmt_rd_rs2 }, - { "sext.w", rv_codec_i, rv_fmt_rd_rs1 }, - { "seqz", rv_codec_i, rv_fmt_rd_rs1 }, - { "snez", rv_codec_r, rv_fmt_rd_rs2 }, - { "sltz", rv_codec_r, rv_fmt_rd_rs1 }, - { "sgtz", rv_codec_r, rv_fmt_rd_rs2 }, - { "fmv.s", rv_codec_r, rv_fmt_frd_frs1 }, - { "fabs.s", rv_codec_r, rv_fmt_frd_frs1 }, - { "fneg.s", rv_codec_r, rv_fmt_frd_frs1 }, - { "fmv.d", rv_codec_r, rv_fmt_frd_frs1 }, - { "fabs.d", rv_codec_r, rv_fmt_frd_frs1 }, - { "fneg.d", rv_codec_r, rv_fmt_frd_frs1 }, - { "fmv.q", rv_codec_r, rv_fmt_frd_frs1 }, - { "fabs.q", rv_codec_r, rv_fmt_frd_frs1 }, - { "fneg.q", rv_codec_r, rv_fmt_frd_frs1 }, - { "beqz", rv_codec_sb, rv_fmt_rs1_offset }, - { "bnez", rv_codec_sb, rv_fmt_rs1_offset }, - { "blez", rv_codec_sb, rv_fmt_rs2_offset }, - { "bgez", rv_codec_sb, rv_fmt_rs1_offset }, - { "bltz", rv_codec_sb, rv_fmt_rs1_offset }, - { "bgtz", rv_codec_sb, rv_fmt_rs2_offset }, - { "jal", rv_codec_none, rv_fmt_offset }, /* rv_op_jal_ra */ - { "jalr", rv_codec_none, rv_fmt_rs1 }, /* rv_op_jalr_ra */ + { "nop", rv_codec_illegal, rv_fmt_none }, + { "mv", rv_codec_illegal, rv_fmt_rd_rs1, rvcp_mv }, + { "not", rv_codec_illegal, rv_fmt_rd_rs1 }, + { "neg", rv_codec_illegal, rv_fmt_rd_rs2 }, + { "negw", rv_codec_illegal, rv_fmt_rd_rs2 }, + { "sext.w", rv_codec_illegal, rv_fmt_rd_rs1 }, + { "seqz", rv_codec_illegal, rv_fmt_rd_rs1 }, + { "snez", rv_codec_illegal, rv_fmt_rd_rs2 }, + { "sltz", rv_codec_illegal, rv_fmt_rd_rs1 }, + { "sgtz", rv_codec_illegal, rv_fmt_rd_rs2 }, + { "fmv.s", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fabs.s", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fneg.s", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fmv.d", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fabs.d", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fneg.d", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fmv.q", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fabs.q", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "fneg.q", rv_codec_illegal, rv_fmt_frd_frs1 }, + { "beqz", rv_codec_illegal, rv_fmt_rs1_offset }, + { "bnez", rv_codec_illegal, rv_fmt_rs1_offset }, + { "blez", rv_codec_illegal, rv_fmt_rs2_offset }, + { "bgez", rv_codec_illegal, rv_fmt_rs1_offset }, + { "bltz", rv_codec_illegal, rv_fmt_rs1_offset }, + { "bgtz", rv_codec_illegal, rv_fmt_rs2_offset }, + { "jal", rv_codec_illegal, rv_fmt_offset }, /* rv_op_jal_ra */ + { "jalr", rv_codec_illegal, rv_fmt_rs1 }, /* rv_op_jalr_ra */ { }, { }, - { "j", rv_codec_uj, rv_fmt_offset }, - { "ret", rv_codec_i, rv_fmt_none }, - { "jr", rv_codec_i, rv_fmt_rs1, rvcp_jr }, + { "j", rv_codec_illegal, rv_fmt_offset }, + { "ret", rv_codec_illegal, rv_fmt_none }, + { "jr", rv_codec_illegal, rv_fmt_rs1, rvcp_jr }, { "rdcycle", rv_codec_i_csr, rv_fmt_rd }, { "rdtime", rv_codec_i_csr, rv_fmt_rd }, { "rdinstret", rv_codec_i_csr, rv_fmt_rd }, @@ -4551,8 +4551,6 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa, rv_inst inst = dec->inst; switch (op->codec) { - case rv_codec_illegal: - break; case rv_codec_none: dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero; dec->imm = 0; @@ -4946,7 +4944,9 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa, dec->rs1 = dec->rs2 = operand_crs1(inst); dec->imm = 0; break; - }; + default: + g_assert_not_reached(); + } } /* check constraint */ From 8594445d444aa6a842194cc15ab88ee73ca08857 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:23 -0700 Subject: [PATCH 781/860] disas/riscv: Simplify some insn decompressions Route "c.j" to "j" instead of "jal", etc. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-34-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index dedd925077..342e2db3cc 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1848,15 +1848,15 @@ static const rv_opcode_data rvi_opcode_data[] = { { "c.and", rv_codec_cs, NULL, DECOMP(rv_op_and) }, { "c.subw", rv_codec_cs, NULL, DECOMP(rv_op_subw) }, { "c.addw", rv_codec_cs, NULL, DECOMP(rv_op_addw) }, - { "c.j", rv_codec_cj, NULL, DECOMP(rv_op_jal) }, - { "c.beqz", rv_codec_cb, NULL, DECOMP(rv_op_beq) }, - { "c.bnez", rv_codec_cb, NULL, DECOMP(rv_op_bne) }, + { "c.j", rv_codec_cj, NULL, DECOMP(rv_op_j) }, + { "c.beqz", rv_codec_cb, NULL, DECOMP(rv_op_beqz) }, + { "c.bnez", rv_codec_cb, NULL, DECOMP(rv_op_bnez) }, { "c.slli", rv_codec_ci_sh6, NULL, DECOMP(rv_op_slli) }, { "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_fld) }, { "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_lw) }, { "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_flw) }, - { "c.jr", rv_codec_cr_jr, NULL, DECOMP(rv_op_jalr) }, - { "c.mv", rv_codec_cr_mv, NULL, DECOMP(rv_op_addi) }, + { "c.jr", rv_codec_cr_jr, NULL, DECOMP(rv_op_jr) }, + { "c.mv", rv_codec_cr_mv, NULL, DECOMP(rv_op_mv) }, { "c.ebreak", rv_codec_ci_none, NULL, DECOMP(rv_op_ebreak) }, { "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(rv_op_jalr) }, { "c.add", rv_codec_cr, NULL, DECOMP(rv_op_add) }, From 6a238ffe354505ad1dd3354a90caa7c64eac6092 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:24 -0700 Subject: [PATCH 782/860] disas/riscv: Store op pointer in rv_comp_data Store a pointer instead of an array index. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-35-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 69 ++++++++++++++++++++++++++------------------------- disas/riscv.h | 8 +++--- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 342e2db3cc..9d05e86c09 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1068,138 +1068,140 @@ static const rvc_constraint rvcc_true[] = { rvc_end }; /* pseudo-instruction metadata */ +static const rv_opcode_data rvi_opcode_data[]; + static const rv_comp_data rvcp_jal[] = { - { rv_op_j, rvcc_j }, - { rv_op_jal_ra, rvcc_jal_ra }, + { &rvi_opcode_data[rv_op_j], rvcc_j }, + { &rvi_opcode_data[rv_op_jal_ra], rvcc_jal_ra }, { }, }; static const rv_comp_data rvcp_jalr[] = { - { rv_op_jr, rvcc_jr }, - { rv_op_jalr_ra, rvcc_jalr_ra }, + { &rvi_opcode_data[rv_op_jr], rvcc_jr }, + { &rvi_opcode_data[rv_op_jalr_ra], rvcc_jalr_ra }, { }, }; static const rv_comp_data rvcp_jr[] = { - { rv_op_ret, rvcc_ret }, + { &rvi_opcode_data[rv_op_ret], rvcc_ret }, { }, }; static const rv_comp_data rvcp_beq[] = { - { rv_op_beqz, rvcc_beqz }, + { &rvi_opcode_data[rv_op_beqz], rvcc_beqz }, { }, }; static const rv_comp_data rvcp_bne[] = { - { rv_op_bnez, rvcc_bnez }, + { &rvi_opcode_data[rv_op_bnez], rvcc_bnez }, { }, }; static const rv_comp_data rvcp_blt[] = { - { rv_op_bltz, rvcc_bltz }, - { rv_op_bgtz, rvcc_bgtz }, + { &rvi_opcode_data[rv_op_bltz], rvcc_bltz }, + { &rvi_opcode_data[rv_op_bgtz], rvcc_bgtz }, { }, }; static const rv_comp_data rvcp_bge[] = { - { rv_op_blez, rvcc_blez }, - { rv_op_bgez, rvcc_bgez }, + { &rvi_opcode_data[rv_op_blez], rvcc_blez }, + { &rvi_opcode_data[rv_op_bgez], rvcc_bgez }, { }, }; static const rv_comp_data rvcp_addi[] = { - { rv_op_mv, rvcc_mv }, + { &rvi_opcode_data[rv_op_mv], rvcc_mv }, { }, }; static const rv_comp_data rvcp_mv[] = { - { rv_op_nop, rvcc_nop }, + { &rvi_opcode_data[rv_op_nop], rvcc_nop }, { }, }; static const rv_comp_data rvcp_sltiu[] = { - { rv_op_seqz, rvcc_seqz }, + { &rvi_opcode_data[rv_op_seqz], rvcc_seqz }, { }, }; static const rv_comp_data rvcp_xori[] = { - { rv_op_not, rvcc_not }, + { &rvi_opcode_data[rv_op_not], rvcc_not }, { }, }; static const rv_comp_data rvcp_sub[] = { - { rv_op_neg, rvcc_neg }, + { &rvi_opcode_data[rv_op_neg], rvcc_neg }, { }, }; static const rv_comp_data rvcp_slt[] = { - { rv_op_sltz, rvcc_sltz }, - { rv_op_sgtz, rvcc_sgtz }, + { &rvi_opcode_data[rv_op_sltz], rvcc_sltz }, + { &rvi_opcode_data[rv_op_sgtz], rvcc_sgtz }, { }, }; static const rv_comp_data rvcp_sltu[] = { - { rv_op_snez, rvcc_snez }, + { &rvi_opcode_data[rv_op_snez], rvcc_snez }, { }, }; static const rv_comp_data rvcp_addiw[] = { - { rv_op_sext_w, rvcc_sext_w }, + { &rvi_opcode_data[rv_op_sext_w], rvcc_sext_w }, { }, }; static const rv_comp_data rvcp_subw[] = { - { rv_op_negw, rvcc_negw }, + { &rvi_opcode_data[rv_op_negw], rvcc_negw }, { }, }; static const rv_comp_data rvcp_fsgnj_s[] = { - { rv_op_fmv_s, rvcc_fmv_s }, + { &rvi_opcode_data[rv_op_fmv_s], rvcc_fmv_s }, { }, }; static const rv_comp_data rvcp_fsgnjn_s[] = { - { rv_op_fneg_s, rvcc_fneg_s }, + { &rvi_opcode_data[rv_op_fneg_s], rvcc_fneg_s }, { }, }; static const rv_comp_data rvcp_fsgnjx_s[] = { - { rv_op_fabs_s, rvcc_fabs_s }, + { &rvi_opcode_data[rv_op_fabs_s], rvcc_fabs_s }, { }, }; static const rv_comp_data rvcp_fsgnj_d[] = { - { rv_op_fmv_d, rvcc_fmv_d }, + { &rvi_opcode_data[rv_op_fmv_d], rvcc_fmv_d }, { }, }; static const rv_comp_data rvcp_fsgnjn_d[] = { - { rv_op_fneg_d, rvcc_fneg_d }, + { &rvi_opcode_data[rv_op_fneg_d], rvcc_fneg_d }, { }, }; static const rv_comp_data rvcp_fsgnjx_d[] = { - { rv_op_fabs_d, rvcc_fabs_d }, + { &rvi_opcode_data[rv_op_fabs_d], rvcc_fabs_d }, { }, }; static const rv_comp_data rvcp_fsgnj_q[] = { - { rv_op_fmv_q, rvcc_fmv_q }, + { &rvi_opcode_data[rv_op_fmv_q], rvcc_fmv_q }, { }, }; static const rv_comp_data rvcp_fsgnjn_q[] = { - { rv_op_fneg_q, rvcc_fneg_q }, + { &rvi_opcode_data[rv_op_fneg_q], rvcc_fneg_q }, { }, }; static const rv_comp_data rvcp_fsgnjx_q[] = { - { rv_op_fabs_q, rvcc_fabs_q }, + { &rvi_opcode_data[rv_op_fabs_q], rvcc_fabs_q }, { }, }; /* Convert compressed insns into normal insns via pseudo expansion. */ -#define DECOMP(X) &(const rv_comp_data){ X, rvcc_true } +#define DECOMP(X) &(const rv_comp_data){ &rvi_opcode_data[X], rvcc_true } /* operand extractors */ @@ -5270,9 +5272,8 @@ static const rv_opcode_data *decode_inst_lift_pseudo(rv_decode *dec, if (comp_data) { while (comp_data->constraints) { if (check_constraints(dec, comp_data->constraints)) { - const rv_opcode_data *new_op = &dec->opcode_data[comp_data->op]; - assert(new_op != op); - return decode_inst_lift_pseudo(dec, new_op); + assert(op != comp_data->op); + return decode_inst_lift_pseudo(dec, comp_data->op); } comp_data++; } diff --git a/disas/riscv.h b/disas/riscv.h index ea7141d3b5..ab62c0db94 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -164,17 +164,19 @@ typedef enum { /* structures */ +typedef struct rv_opcode_data rv_opcode_data; + typedef struct { - int op; + const rv_opcode_data *op; const rvc_constraint *constraints; } rv_comp_data; -typedef struct { +struct rv_opcode_data { const char *name; rv_codec codec; const char *format; const rv_comp_data *pseudo; -} rv_opcode_data; +}; typedef struct { const RISCVCPUConfig *cfg; From 850708045ab30e2252e30a647a822a5d0e6f2cbd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:25 -0700 Subject: [PATCH 783/860] disas/riscv: Return rv_opcode_data pointer from decoders Rather than putting array + index into rv_decode, return the pointer to the object directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-36-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-xlrbr.c | 5 ++-- disas/riscv-xlrbr.h | 2 +- disas/riscv-xthead.c | 55 +++++++++++++++++------------------------- disas/riscv-xthead.h | 22 ++++++++--------- disas/riscv-xventana.c | 5 ++-- disas/riscv-xventana.h | 2 +- disas/riscv.c | 23 +++++++----------- disas/riscv.h | 2 -- 8 files changed, 48 insertions(+), 68 deletions(-) diff --git a/disas/riscv-xlrbr.c b/disas/riscv-xlrbr.c index d0092e9edd..b12916c9da 100644 --- a/disas/riscv-xlrbr.c +++ b/disas/riscv-xlrbr.c @@ -36,7 +36,7 @@ static const rv_opcode_data xlrbr_opcode_data[] = { { "crc32c.d", rv_codec_r, rv_fmt_rd_rs1 }, }; -void decode_xlrbr(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xlrbr(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -76,6 +76,5 @@ void decode_xlrbr(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xlrbr_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xlrbr_opcode_data[op]; } diff --git a/disas/riscv-xlrbr.h b/disas/riscv-xlrbr.h index ebf3d4c133..da14f1b340 100644 --- a/disas/riscv-xlrbr.h +++ b/disas/riscv-xlrbr.h @@ -12,6 +12,6 @@ #include "disas/riscv.h" -void decode_xlrbr(rv_decode *, rv_isa); +const rv_opcode_data *decode_xlrbr(rv_decode *, rv_isa); #endif /* DISAS_RISCV_XLRBR_H */ diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c index 757d6ff39f..428a6380e9 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -246,7 +246,7 @@ static const rv_opcode_data xthead_opcode_data[] = { { "th.sync.s", rv_codec_none, rv_fmt_none }, }; -void decode_xtheadba(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadba(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -272,11 +272,10 @@ void decode_xtheadba(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadbb(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadbb(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -331,11 +330,10 @@ void decode_xtheadbb(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadbs(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadbs(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -358,11 +356,10 @@ void decode_xtheadbs(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadcmo(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadcmo(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -437,11 +434,10 @@ void decode_xtheadcmo(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadcondmov(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -465,11 +461,10 @@ void decode_xtheadcondmov(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -503,11 +498,10 @@ void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadfmv(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadfmv(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -539,11 +533,10 @@ void decode_xtheadfmv(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadmac(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadmac(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -571,11 +564,10 @@ void decode_xtheadmac(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadmemidx(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -646,11 +638,10 @@ void decode_xtheadmemidx(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadmempair(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadmempair(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -681,11 +672,10 @@ void decode_xtheadmempair(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } -void decode_xtheadsync(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xtheadsync(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -716,6 +706,5 @@ void decode_xtheadsync(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = xthead_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; } diff --git a/disas/riscv-xthead.h b/disas/riscv-xthead.h index 0c65c8e0af..f2160be8a4 100644 --- a/disas/riscv-xthead.h +++ b/disas/riscv-xthead.h @@ -11,16 +11,16 @@ #include "disas/riscv.h" -void decode_xtheadba(rv_decode *, rv_isa); -void decode_xtheadbb(rv_decode *, rv_isa); -void decode_xtheadbs(rv_decode *, rv_isa); -void decode_xtheadcmo(rv_decode *, rv_isa); -void decode_xtheadcondmov(rv_decode *, rv_isa); -void decode_xtheadfmemidx(rv_decode *, rv_isa); -void decode_xtheadfmv(rv_decode *, rv_isa); -void decode_xtheadmac(rv_decode *, rv_isa); -void decode_xtheadmemidx(rv_decode *, rv_isa); -void decode_xtheadmempair(rv_decode *, rv_isa); -void decode_xtheadsync(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadba(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadbb(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadbs(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadcmo(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadcondmov(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadfmemidx(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadfmv(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadmac(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadmemidx(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadmempair(rv_decode *, rv_isa); +const rv_opcode_data *decode_xtheadsync(rv_decode *, rv_isa); #endif /* DISAS_RISCV_XTHEAD_H */ diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c index 4fb9a29d0b..a4e59312d0 100644 --- a/disas/riscv-xventana.c +++ b/disas/riscv-xventana.c @@ -20,7 +20,7 @@ static const rv_opcode_data ventana_opcode_data[] = { { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2 }, }; -void decode_xventanacondops(rv_decode *dec, rv_isa isa) +const rv_opcode_data *decode_xventanacondops(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -38,6 +38,5 @@ void decode_xventanacondops(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = ventana_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &ventana_opcode_data[op]; } diff --git a/disas/riscv-xventana.h b/disas/riscv-xventana.h index 1006c3be00..d28e84c7e4 100644 --- a/disas/riscv-xventana.h +++ b/disas/riscv-xventana.h @@ -11,6 +11,6 @@ #include "disas/riscv.h" -void decode_xventanacondops(rv_decode*, rv_isa); +const rv_opcode_data *decode_xventanacondops(rv_decode*, rv_isa); #endif /* DISAS_RISCV_XVENTANA_H */ diff --git a/disas/riscv.c b/disas/riscv.c index 9d05e86c09..bc86cb866a 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2846,7 +2846,7 @@ static const char *csr_name(int csrno) /* decode opcode */ -static void decode_inst_opcode(rv_decode *dec, rv_isa isa) +static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; rv_opcode op = rv_op_illegal; @@ -4541,8 +4541,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) break; } - dec->opcode_data = rvi_opcode_data; - dec->op = op; + return op == rv_op_illegal ? NULL : &rvi_opcode_data[op]; } /* decode operands */ @@ -5291,14 +5290,12 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, .inst = inst, .cfg = cfg, }; - const rv_opcode_data *op; + const rv_opcode_data *op = decode_inst_opcode(&dec, isa); - decode_inst_opcode(&dec, isa); - - if (dec.op == rv_op_illegal && cfg) { + if (!op && cfg) { static const struct { bool (*guard_func)(const RISCVCPUConfig *); - void (*decode_func)(rv_decode *, rv_isa); + const rv_opcode_data *(*decode_func)(rv_decode *, rv_isa); } decoders[] = { { has_xtheadba_p, decode_xtheadba }, { has_xtheadbb_p, decode_xtheadbb }, @@ -5317,19 +5314,17 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) { if (decoders[i].guard_func(cfg)) { - decoders[i].decode_func(&dec, isa); - if (dec.op != rv_op_illegal) { + op = decoders[i].decode_func(&dec, isa); + if (op) { break; } } } } - - if (dec.op == rv_op_illegal) { - dec.opcode_data = rvi_opcode_data; + if (!op) { + op = &rvi_opcode_data[rv_op_illegal]; } - op = &dec.opcode_data[dec.op]; decode_inst_operands(&dec, isa, op); op = decode_inst_lift_pseudo(&dec, op); return format_inst(24, &dec, op); diff --git a/disas/riscv.h b/disas/riscv.h index ab62c0db94..241dd47d7f 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -182,10 +182,8 @@ typedef struct { const RISCVCPUConfig *cfg; uint64_t pc; uint64_t inst; - const rv_opcode_data *opcode_data; int32_t imm; int32_t imm1; - uint16_t op; uint8_t rd; uint8_t rs1; uint8_t rs2; From 0da84325e472b43169135010b3dd75e85ca37092 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:26 -0700 Subject: [PATCH 784/860] disas/riscv: Skip post-processing of illegal insns Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Message-ID: <20260812223142.349142-37-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index bc86cb866a..75c2fa11e6 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -5321,12 +5321,14 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, } } } - if (!op) { + + if (op) { + decode_inst_operands(&dec, isa, op); + op = decode_inst_lift_pseudo(&dec, op); + } else { op = &rvi_opcode_data[rv_op_illegal]; } - decode_inst_operands(&dec, isa, op); - op = decode_inst_lift_pseudo(&dec, op); return format_inst(24, &dec, op); } From 78f51b0ea5205bda472652b326caf439d92a2c3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:27 -0700 Subject: [PATCH 785/860] disas/riscv: Reject all of OP-32 and OP-IMM-32 for RV32 These entire base opcodes are RV64. Reject them all at once rather than one at a time. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-38-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/disas/riscv.c b/disas/riscv.c index 75c2fa11e6..e650ad7688 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -3311,6 +3311,10 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) } break; case 6: + /* OP-IMM-32 */ + if (isa == rv32) { + break; + } switch ((inst >> 12) & 0b111) { case 0: op = rv_op_addiw; break; case 1: @@ -3557,6 +3561,10 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 13: op = rv_op_lui; break; case 14: + /* OP-32 */ + if (isa == rv32) { + break; + } switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { case 0: op = rv_op_addw; break; From d4151cf2675124e26b3383ae06f7352a23f17399 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:28 -0700 Subject: [PATCH 786/860] disas/riscv: Fix rv32 encoding of zext.h For rv64, pack with rs2 = 0 does not encode zext.h. Cc: qemu-stable@nongnu.org Fixes: 02c1b569a15 ("disas/riscv: Add Zb[abcs] instructions") Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-39-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index e650ad7688..5624e7f60a 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -3506,9 +3506,10 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) case 14: op = rv_op_rem; break; case 15: op = rv_op_remu; break; case 36: - switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_zext_h; break; - default: op = rv_op_pack; break; + if (isa == rv32 && !((inst >> 20) & 0b11111)) { + op = rv_op_zext_h; + } else { + op = rv_op_pack; } break; case 39: op = rv_op_packh; break; From d610e55c8390a8bfd15d0a4f1521d7fc46e4e0b7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:29 -0700 Subject: [PATCH 787/860] disas/riscv: Fix isa decoding of rev8 The encoding of rev8 is different for rv32 and rv64. Cc: qemu-stable@nongnu.org Fixes: 02c1b569a15 ("disas/riscv: Add Zb[abcs] instructions") Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-40-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 5624e7f60a..787340e811 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -3292,8 +3292,16 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0b01100: op = rv_op_rori; break; case 0b01101: switch ((inst >> 20) & 0b1111111) { - case 0b0011000: op = rv_op_rev8; break; - case 0b0111000: op = rv_op_rev8; break; + case 0b0011000: + if (isa == rv32) { + op = rv_op_rev8; + } + break; + case 0b0111000: + if (isa == rv64) { + op = rv_op_rev8; + } + break; case 0b0000111: op = rv_op_brev8; break; } break; From ecbd3a031f12669da98cdf929cbc7dbb88d3450f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:30 -0700 Subject: [PATCH 788/860] disas/riscv: Fix typo in th.lbib format th.lbib should format the same as th.lbia, and the other increment insns, with the address register in parenthesis. Cc: qemu-stable@nongnu.org Fixes: 318df7238b9 ("disas/riscv: Add support for XThead* instructions") Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-41-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-xthead.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c index 428a6380e9..fbc96631fd 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -189,7 +189,7 @@ static const rv_opcode_data xthead_opcode_data[] = { { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, /* XTheadMemIdx */ { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml }, + { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, From 7a1cca6360e39cc26b9e31d7af613270d6a0cf25 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:31 -0700 Subject: [PATCH 789/860] disas/riscv: Split ventana_opcode_data Move the table to riscv-xventana-op.c.inc and massage the lines into OP() form. Drop vt.illegal as unused. Return pointers to objects directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-42-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-xventana-op.c.inc | 2 ++ disas/riscv-xventana.c | 21 ++++++--------------- 2 files changed, 8 insertions(+), 15 deletions(-) create mode 100644 disas/riscv-xventana-op.c.inc diff --git a/disas/riscv-xventana-op.c.inc b/disas/riscv-xventana-op.c.inc new file mode 100644 index 0000000000..0fe4666aee --- /dev/null +++ b/disas/riscv-xventana-op.c.inc @@ -0,0 +1,2 @@ +OP(vt_maskc, "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(vt_maskcn, "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2) diff --git a/disas/riscv-xventana.c b/disas/riscv-xventana.c index a4e59312d0..5d2e07865f 100644 --- a/disas/riscv-xventana.c +++ b/disas/riscv-xventana.c @@ -8,35 +8,26 @@ #include "disas/riscv.h" #include "disas/riscv-xventana.h" -typedef enum { - /* 0 is reserved for rv_op_illegal. */ - ventana_op_vt_maskc = 1, - ventana_op_vt_maskcn = 2, -} rv_ventana_op; - -static const rv_opcode_data ventana_opcode_data[] = { - { "vt.illegal", rv_codec_illegal, rv_fmt_none }, - { "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2 }, -}; +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; +#include "riscv-xventana-op.c.inc" +#undef OP const rv_opcode_data *decode_xventanacondops(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: switch (((inst >> 2) & 0b11111)) { case 30: switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 6: op = ventana_op_vt_maskc; break; - case 7: op = ventana_op_vt_maskcn; break; + case 6: return &op_vt_maskc; + case 7: return &op_vt_maskcn; } break; } break; } - return op == rv_op_illegal ? NULL : &ventana_opcode_data[op]; + return NULL; } From 5fa5e2df676c68def8d0284eb762f7d1ccebfd44 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:32 -0700 Subject: [PATCH 790/860] disas/riscv: Split xlrbr_opcode_data Move the table to riscv-xlrbr-op.c.inc and massage the lines into OP() form. Drop illegal as unused. Return pointers to objects directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-43-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-xlrbr-op.c.inc | 8 ++++++ disas/riscv-xlrbr.c | 54 +++++++++----------------------------- 2 files changed, 20 insertions(+), 42 deletions(-) create mode 100644 disas/riscv-xlrbr-op.c.inc diff --git a/disas/riscv-xlrbr-op.c.inc b/disas/riscv-xlrbr-op.c.inc new file mode 100644 index 0000000000..57add37c24 --- /dev/null +++ b/disas/riscv-xlrbr-op.c.inc @@ -0,0 +1,8 @@ +OP(crc32_b, "crc32.b", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32_h, "crc32.h", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32_w, "crc32.w", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32_d, "crc32.d", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_b, "crc32c.b", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_h, "crc32c.h", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_w, "crc32c.w", rv_codec_r, rv_fmt_rd_rs1) +OP(crc32c_d, "crc32c.d", rv_codec_r, rv_fmt_rd_rs1) diff --git a/disas/riscv-xlrbr.c b/disas/riscv-xlrbr.c index b12916c9da..b4229e5c83 100644 --- a/disas/riscv-xlrbr.c +++ b/disas/riscv-xlrbr.c @@ -8,38 +8,16 @@ */ #include "qemu/osdep.h" - #include "disas/riscv.h" #include "disas/riscv-xlrbr.h" -typedef enum { - /* 0 is reserved for rv_op_illegal. */ - rv_op_crc32_b = 1, - rv_op_crc32_h = 2, - rv_op_crc32_w = 3, - rv_op_crc32_d = 4, - rv_op_crc32c_b = 5, - rv_op_crc32c_h = 6, - rv_op_crc32c_w = 7, - rv_op_crc32c_d = 8, -} rv_xlrbr_op; - -static const rv_opcode_data xlrbr_opcode_data[] = { - { "illegal", rv_codec_illegal, rv_fmt_none }, - { "crc32.b", rv_codec_r, rv_fmt_rd_rs1 }, - { "crc32.h", rv_codec_r, rv_fmt_rd_rs1 }, - { "crc32.w", rv_codec_r, rv_fmt_rd_rs1 }, - { "crc32.d", rv_codec_r, rv_fmt_rd_rs1 }, - { "crc32c.b", rv_codec_r, rv_fmt_rd_rs1 }, - { "crc32c.h", rv_codec_r, rv_fmt_rd_rs1 }, - { "crc32c.w", rv_codec_r, rv_fmt_rd_rs1 }, - { "crc32c.d", rv_codec_r, rv_fmt_rd_rs1 }, -}; +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; +#include "riscv-xlrbr-op.c.inc" +#undef OP const rv_opcode_data *decode_xlrbr(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch ((inst >> 0) & 0b1111111) { case 0b0010011: @@ -47,34 +25,26 @@ const rv_opcode_data *decode_xlrbr(rv_decode *dec, rv_isa isa) case 0b001: switch ((inst >> 20 & 0b111111111111)) { case 0b011000010000: - op = rv_op_crc32_b; - break; + return &op_crc32_b; case 0b011000010001: - op = rv_op_crc32_h; - break; + return &op_crc32_h; case 0b011000010010: - op = rv_op_crc32_w; - break; + return &op_crc32_w; case 0b011000010011: - op = rv_op_crc32_d; - break; + return &op_crc32_d; case 0b011000011000: - op = rv_op_crc32c_b; - break; + return &op_crc32c_b; case 0b011000011001: - op = rv_op_crc32c_h; - break; + return &op_crc32c_h; case 0b011000011010: - op = rv_op_crc32c_w; - break; + return &op_crc32c_w; case 0b011000011011: - op = rv_op_crc32c_d; - break; + return &op_crc32c_d; } break; } break; } - return op == rv_op_illegal ? NULL : &xlrbr_opcode_data[op]; + return NULL; } From 282d8cca8b8475885c126a82627e783089e7f520 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:33 -0700 Subject: [PATCH 791/860] disas/riscv: Split xthead_opcode_data Move the table to riscv-xthead-op.c.inc and massage the lines into OP() form. Drop th.illegal as unused. Return pointers to objects directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-44-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-xthead-op.c.inc | 125 ++++++++++ disas/riscv-xthead.c | 481 +++++++++--------------------------- 2 files changed, 243 insertions(+), 363 deletions(-) create mode 100644 disas/riscv-xthead-op.c.inc diff --git a/disas/riscv-xthead-op.c.inc b/disas/riscv-xthead-op.c.inc new file mode 100644 index 0000000000..1e4e0557cd --- /dev/null +++ b/disas/riscv-xthead-op.c.inc @@ -0,0 +1,125 @@ +/* XTheadBa */ +OP(th_addsl, "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) + +/* XTheadBb */ +OP(th_srri, "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm) +OP(th_srriw, "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm) +OP(th_ext, "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml) +OP(th_extu, "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml) +OP(th_ff0, "th.ff0", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_ff1, "th.ff1", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_rev, "th.rev", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_revw, "th.revw", rv_codec_r2, rv_fmt_rd_rs1) +OP(th_tstnbz, "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1) + +/* XTheadBs */ +OP(th_tst, "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm) + +/* XTheadCmo */ +OP(th_dcache_call, "th.dcache.call", rv_codec_none, rv_fmt_none) +OP(th_dcache_ciall, "th.dcache.ciall", rv_codec_none, rv_fmt_none) +OP(th_dcache_iall, "th.dcache.iall", rv_codec_none, rv_fmt_none) +OP(th_dcache_cpa, "th.dcache.cpa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cipa, "th.dcache.cipa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_ipa, "th.dcache.ipa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cva, "th.dcache.cva", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_civa, "th.dcache.civa", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_iva, "th.dcache.iva", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_csw, "th.dcache.csw", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cisw, "th.dcache.cisw", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_isw, "th.dcache.isw", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cpal1, "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1) +OP(th_dcache_cval1, "th.dcache.cval1", rv_codec_r, rv_fmt_rs1) +OP(th_icache_iall, "th.icache.iall", rv_codec_none, rv_fmt_none) +OP(th_icache_ialls, "th.icache.ialls", rv_codec_none, rv_fmt_none) +OP(th_icache_ipa, "th.icache.ipa", rv_codec_r, rv_fmt_rs1) +OP(th_icache_iva, "th.icache.iva", rv_codec_r, rv_fmt_rs1) +OP(th_l2cache_call, "th.l2cache.call", rv_codec_none, rv_fmt_none) +OP(th_l2cache_ciall, "th.l2cache.ciall", rv_codec_none, rv_fmt_none) +OP(th_l2cache_iall, "th.l2cache.iall", rv_codec_none, rv_fmt_none) + +/* XTheadCondMov */ +OP(th_mveqz, "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mvnez, "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2) + +/* XTheadFMemIdx */ +OP(th_flrd, "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_flrw, "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_flurd, "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_flurw, "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsrd, "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsrw, "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsurd, "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) +OP(th_fsurw, "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm) + +/* XTheadFmv */ +OP(th_fmv_hw_x, "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1) +OP(th_fmv_x_hw, "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1) + +/* XTheadMac */ +OP(th_mula, "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulaw, "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulah, "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_muls, "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulsw, "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(th_mulsh, "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2) + +/* XTheadMemIdx */ +OP(th_lbia, "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lbib, "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lbuia, "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lbuib, "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhia, "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhib, "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhuia, "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lhuib, "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwia, "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwib, "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwuia, "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lwuib, "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_ldia, "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_ldib, "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sbia, "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sbib, "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_shia, "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_shib, "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_swia, "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_swib, "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sdia, "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_sdib, "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr) +OP(th_lrb, "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrbu, "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrh, "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrhu, "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrw, "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrwu, "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lrd, "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srb, "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srh, "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srw, "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_srd, "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurb, "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurbu, "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurh, "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurhu, "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurw, "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurwu, "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_lurd, "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surb, "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surh, "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surw, "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) +OP(th_surd, "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm) + +/* XTheadMemPair */ +OP(th_ldd, "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_lwd, "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_lwud, "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_sdd, "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm) +OP(th_swd, "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm) + +/* XTheadSync */ +OP(th_sfence_vmas, "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2) +OP(th_sync, "th.sync", rv_codec_none, rv_fmt_none) +OP(th_sync_i, "th.sync.i", rv_codec_none, rv_fmt_none) +OP(th_sync_is, "th.sync.is", rv_codec_none, rv_fmt_none) +OP(th_sync_s, "th.sync.s", rv_codec_none, rv_fmt_none) diff --git a/disas/riscv-xthead.c b/disas/riscv-xthead.c index fbc96631fd..387c22c869 100644 --- a/disas/riscv-xthead.c +++ b/disas/riscv-xthead.c @@ -8,248 +8,13 @@ #include "disas/riscv.h" #include "disas/riscv-xthead.h" -typedef enum { - /* 0 is reserved for rv_op_illegal. */ - /* XTheadBa */ - rv_op_th_addsl = 1, - /* XTheadBb */ - rv_op_th_srri, - rv_op_th_srriw, - rv_op_th_ext, - rv_op_th_extu, - rv_op_th_ff0, - rv_op_th_ff1, - rv_op_th_rev, - rv_op_th_revw, - rv_op_th_tstnbz, - /* XTheadBs */ - rv_op_th_tst, - /* XTheadCmo */ - rv_op_th_dcache_call, - rv_op_th_dcache_ciall, - rv_op_th_dcache_iall, - rv_op_th_dcache_cpa, - rv_op_th_dcache_cipa, - rv_op_th_dcache_ipa, - rv_op_th_dcache_cva, - rv_op_th_dcache_civa, - rv_op_th_dcache_iva, - rv_op_th_dcache_csw, - rv_op_th_dcache_cisw, - rv_op_th_dcache_isw, - rv_op_th_dcache_cpal1, - rv_op_th_dcache_cval1, - rv_op_th_icache_iall, - rv_op_th_icache_ialls, - rv_op_th_icache_ipa, - rv_op_th_icache_iva, - rv_op_th_l2cache_call, - rv_op_th_l2cache_ciall, - rv_op_th_l2cache_iall, - /* XTheadCondMov */ - rv_op_th_mveqz, - rv_op_th_mvnez, - /* XTheadFMemIdx */ - rv_op_th_flrd, - rv_op_th_flrw, - rv_op_th_flurd, - rv_op_th_flurw, - rv_op_th_fsrd, - rv_op_th_fsrw, - rv_op_th_fsurd, - rv_op_th_fsurw, - /* XTheadFmv */ - rv_op_th_fmv_hw_x, - rv_op_th_fmv_x_hw, - /* XTheadMac */ - rv_op_th_mula, - rv_op_th_mulah, - rv_op_th_mulaw, - rv_op_th_muls, - rv_op_th_mulsw, - rv_op_th_mulsh, - /* XTheadMemIdx */ - rv_op_th_lbia, - rv_op_th_lbib, - rv_op_th_lbuia, - rv_op_th_lbuib, - rv_op_th_lhia, - rv_op_th_lhib, - rv_op_th_lhuia, - rv_op_th_lhuib, - rv_op_th_lwia, - rv_op_th_lwib, - rv_op_th_lwuia, - rv_op_th_lwuib, - rv_op_th_ldia, - rv_op_th_ldib, - rv_op_th_sbia, - rv_op_th_sbib, - rv_op_th_shia, - rv_op_th_shib, - rv_op_th_swia, - rv_op_th_swib, - rv_op_th_sdia, - rv_op_th_sdib, - rv_op_th_lrb, - rv_op_th_lrbu, - rv_op_th_lrh, - rv_op_th_lrhu, - rv_op_th_lrw, - rv_op_th_lrwu, - rv_op_th_lrd, - rv_op_th_srb, - rv_op_th_srh, - rv_op_th_srw, - rv_op_th_srd, - rv_op_th_lurb, - rv_op_th_lurbu, - rv_op_th_lurh, - rv_op_th_lurhu, - rv_op_th_lurw, - rv_op_th_lurwu, - rv_op_th_lurd, - rv_op_th_surb, - rv_op_th_surh, - rv_op_th_surw, - rv_op_th_surd, - /* XTheadMemPair */ - rv_op_th_ldd, - rv_op_th_lwd, - rv_op_th_lwud, - rv_op_th_sdd, - rv_op_th_swd, - /* XTheadSync */ - rv_op_th_sfence_vmas, - rv_op_th_sync, - rv_op_th_sync_i, - rv_op_th_sync_is, - rv_op_th_sync_s, -} rv_xthead_op; - -static const rv_opcode_data xthead_opcode_data[] = { - { "th.illegal", rv_codec_illegal, rv_fmt_none }, - /* XTheadBa */ - { "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - /* XTheadBb */ - { "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm }, - { "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm }, - { "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml }, - { "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml }, - { "th.ff0", rv_codec_r2, rv_fmt_rd_rs1 }, - { "th.ff1", rv_codec_r2, rv_fmt_rd_rs1 }, - { "th.rev", rv_codec_r2, rv_fmt_rd_rs1 }, - { "th.revw", rv_codec_r2, rv_fmt_rd_rs1 }, - { "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1 }, - /* XTheadBs */ - { "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm }, - /* XTheadCmo */ - { "th.dcache.call", rv_codec_none, rv_fmt_none }, - { "th.dcache.ciall", rv_codec_none, rv_fmt_none }, - { "th.dcache.iall", rv_codec_none, rv_fmt_none }, - { "th.dcache.cpa", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.cipa", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.ipa", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.cva", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.civa", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.iva", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.csw", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.cisw", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.isw", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1 }, - { "th.dcache.cval1", rv_codec_r, rv_fmt_rs1 }, - { "th.icache.iall", rv_codec_none, rv_fmt_none }, - { "th.icache.ialls", rv_codec_none, rv_fmt_none }, - { "th.icache.ipa", rv_codec_r, rv_fmt_rs1 }, - { "th.icache.iva", rv_codec_r, rv_fmt_rs1 }, - { "th.l2cache.call", rv_codec_none, rv_fmt_none }, - { "th.l2cache.ciall", rv_codec_none, rv_fmt_none }, - { "th.l2cache.iall", rv_codec_none, rv_fmt_none }, - /* XTheadCondMov */ - { "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - /* XTheadFMemIdx */ - { "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - { "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - { "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - { "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - { "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - { "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - { "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - { "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm }, - /* XTheadFmv */ - { "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1 }, - { "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1 }, - /* XTheadMac */ - { "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - /* XTheadMemIdx */ - { "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr }, - { "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - { "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm }, - /* XTheadMemPair */ - { "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm }, - { "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm }, - { "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm }, - { "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm }, - { "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm }, - /* XTheadSync */ - { "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2 }, - { "th.sync", rv_codec_none, rv_fmt_none }, - { "th.sync.i", rv_codec_none, rv_fmt_none }, - { "th.sync.is", rv_codec_none, rv_fmt_none }, - { "th.sync.s", rv_codec_none, rv_fmt_none }, -}; +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; +#include "riscv-xthead-op.c.inc" +#undef OP const rv_opcode_data *decode_xtheadba(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -262,7 +27,7 @@ const rv_opcode_data *decode_xtheadba(rv_decode *dec, rv_isa isa) case 0b0000000: case 0b0000001: case 0b0000010: - case 0b0000011: op = rv_op_th_addsl; break; + case 0b0000011: return &op_th_addsl; } break; } @@ -272,13 +37,12 @@ const rv_opcode_data *decode_xtheadba(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadbb(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -288,41 +52,41 @@ const rv_opcode_data *decode_xtheadbb(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 25) & 0b1111111) { - case 0b0001010: op = rv_op_th_srriw; break; + case 0b0001010: return &op_th_srriw; case 0b1000000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_tstnbz; + return &op_th_tstnbz; } break; case 0b1000001: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_rev; + return &op_th_rev; } break; case 0b1000010: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_ff0; + return &op_th_ff0; } break; case 0b1000011: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_ff1; + return &op_th_ff1; } break; case 0b1000100: case 0b1001000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_revw; + return &op_th_revw; } break; case 0b0001000: case 0b0001001: - op = rv_op_th_srri; + return &op_th_srri; break; } break; - case 2: op = rv_op_th_ext; break; - case 3: op = rv_op_th_extu; break; + case 2: return &op_th_ext; + case 3: return &op_th_extu; } break; /* custom-0 */ @@ -330,13 +94,12 @@ const rv_opcode_data *decode_xtheadbb(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadbs(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -346,7 +109,7 @@ const rv_opcode_data *decode_xtheadbs(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 26) & 0b111111) { - case 0b100010: op = rv_op_th_tst; break; + case 0b100010: return &op_th_tst; } break; } @@ -356,13 +119,12 @@ const rv_opcode_data *decode_xtheadbs(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadcmo(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -374,55 +136,55 @@ const rv_opcode_data *decode_xtheadcmo(rv_decode *dec, rv_isa isa) switch ((inst >> 20 & 0b111111111111)) { case 0b000000000001: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_dcache_call; + return &op_th_dcache_call; } break; case 0b000000000011: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_dcache_ciall; + return &op_th_dcache_ciall; } break; case 0b000000000010: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_dcache_iall; + return &op_th_dcache_iall; } break; - case 0b000000101001: op = rv_op_th_dcache_cpa; break; - case 0b000000101011: op = rv_op_th_dcache_cipa; break; - case 0b000000101010: op = rv_op_th_dcache_ipa; break; - case 0b000000100101: op = rv_op_th_dcache_cva; break; - case 0b000000100111: op = rv_op_th_dcache_civa; break; - case 0b000000100110: op = rv_op_th_dcache_iva; break; - case 0b000000100001: op = rv_op_th_dcache_csw; break; - case 0b000000100011: op = rv_op_th_dcache_cisw; break; - case 0b000000100010: op = rv_op_th_dcache_isw; break; - case 0b000000101000: op = rv_op_th_dcache_cpal1; break; - case 0b000000100100: op = rv_op_th_dcache_cval1; break; + case 0b000000101001: return &op_th_dcache_cpa; + case 0b000000101011: return &op_th_dcache_cipa; + case 0b000000101010: return &op_th_dcache_ipa; + case 0b000000100101: return &op_th_dcache_cva; + case 0b000000100111: return &op_th_dcache_civa; + case 0b000000100110: return &op_th_dcache_iva; + case 0b000000100001: return &op_th_dcache_csw; + case 0b000000100011: return &op_th_dcache_cisw; + case 0b000000100010: return &op_th_dcache_isw; + case 0b000000101000: return &op_th_dcache_cpal1; + case 0b000000100100: return &op_th_dcache_cval1; case 0b000000010000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_icache_iall; + return &op_th_icache_iall; } break; case 0b000000010001: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_icache_ialls; + return &op_th_icache_ialls; } break; - case 0b000000111000: op = rv_op_th_icache_ipa; break; - case 0b000000110000: op = rv_op_th_icache_iva; break; + case 0b000000111000: return &op_th_icache_ipa; + case 0b000000110000: return &op_th_icache_iva; case 0b000000010101: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_l2cache_call; + return &op_th_l2cache_call; } break; case 0b000000010111: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_l2cache_ciall; + return &op_th_l2cache_ciall; } break; case 0b000000010110: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_l2cache_iall; + return &op_th_l2cache_iall; } break; } @@ -434,13 +196,12 @@ const rv_opcode_data *decode_xtheadcmo(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadcondmov(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -450,8 +211,8 @@ const rv_opcode_data *decode_xtheadcondmov(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 25) & 0b1111111) { - case 0b0100000: op = rv_op_th_mveqz; break; - case 0b0100001: op = rv_op_th_mvnez; break; + case 0b0100000: return &op_th_mveqz; + case 0b0100001: return &op_th_mvnez; } break; } @@ -461,13 +222,12 @@ const rv_opcode_data *decode_xtheadcondmov(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -477,18 +237,18 @@ const rv_opcode_data *decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 6: switch ((inst >> 27) & 0b11111) { - case 8: op = rv_op_th_flrw; break; - case 10: op = rv_op_th_flurw; break; - case 12: op = rv_op_th_flrd; break; - case 14: op = rv_op_th_flurd; break; + case 8: return &op_th_flrw; + case 10: return &op_th_flurw; + case 12: return &op_th_flrd; + case 14: return &op_th_flurd; } break; case 7: switch ((inst >> 27) & 0b11111) { - case 8: op = rv_op_th_fsrw; break; - case 10: op = rv_op_th_fsurw; break; - case 12: op = rv_op_th_fsrd; break; - case 14: op = rv_op_th_fsurd; break; + case 8: return &op_th_fsrw; + case 10: return &op_th_fsurw; + case 12: return &op_th_fsrd; + case 14: return &op_th_fsurd; } break; } @@ -498,13 +258,12 @@ const rv_opcode_data *decode_xtheadfmemidx(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadfmv(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -516,12 +275,12 @@ const rv_opcode_data *decode_xtheadfmv(rv_decode *dec, rv_isa isa) switch ((inst >> 25) & 0b1111111) { case 0b1010000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_fmv_hw_x; + return &op_th_fmv_hw_x; } break; case 0b1100000: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_th_fmv_x_hw; + return &op_th_fmv_x_hw; } break; } @@ -533,13 +292,12 @@ const rv_opcode_data *decode_xtheadfmv(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadmac(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -549,12 +307,12 @@ const rv_opcode_data *decode_xtheadmac(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 1: switch ((inst >> 25) & 0b1111111) { - case 0b0010000: op = rv_op_th_mula; break; - case 0b0010001: op = rv_op_th_muls; break; - case 0b0010010: op = rv_op_th_mulaw; break; - case 0b0010011: op = rv_op_th_mulsw; break; - case 0b0010100: op = rv_op_th_mulah; break; - case 0b0010101: op = rv_op_th_mulsh; break; + case 0b0010000: return &op_th_mula; + case 0b0010001: return &op_th_muls; + case 0b0010010: return &op_th_mulaw; + case 0b0010011: return &op_th_mulsw; + case 0b0010100: return &op_th_mulah; + case 0b0010101: return &op_th_mulsh; } break; } @@ -564,13 +322,12 @@ const rv_opcode_data *decode_xtheadmac(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadmemidx(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -580,54 +337,54 @@ const rv_opcode_data *decode_xtheadmemidx(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 4: switch ((inst >> 27) & 0b11111) { - case 0: op = rv_op_th_lrb; break; - case 1: op = rv_op_th_lbib; break; - case 2: op = rv_op_th_lurb; break; - case 3: op = rv_op_th_lbia; break; - case 4: op = rv_op_th_lrh; break; - case 5: op = rv_op_th_lhib; break; - case 6: op = rv_op_th_lurh; break; - case 7: op = rv_op_th_lhia; break; - case 8: op = rv_op_th_lrw; break; - case 9: op = rv_op_th_lwib; break; - case 10: op = rv_op_th_lurw; break; - case 11: op = rv_op_th_lwia; break; - case 12: op = rv_op_th_lrd; break; - case 13: op = rv_op_th_ldib; break; - case 14: op = rv_op_th_lurd; break; - case 15: op = rv_op_th_ldia; break; - case 16: op = rv_op_th_lrbu; break; - case 17: op = rv_op_th_lbuib; break; - case 18: op = rv_op_th_lurbu; break; - case 19: op = rv_op_th_lbuia; break; - case 20: op = rv_op_th_lrhu; break; - case 21: op = rv_op_th_lhuib; break; - case 22: op = rv_op_th_lurhu; break; - case 23: op = rv_op_th_lhuia; break; - case 24: op = rv_op_th_lrwu; break; - case 25: op = rv_op_th_lwuib; break; - case 26: op = rv_op_th_lurwu; break; - case 27: op = rv_op_th_lwuia; break; + case 0: return &op_th_lrb; + case 1: return &op_th_lbib; + case 2: return &op_th_lurb; + case 3: return &op_th_lbia; + case 4: return &op_th_lrh; + case 5: return &op_th_lhib; + case 6: return &op_th_lurh; + case 7: return &op_th_lhia; + case 8: return &op_th_lrw; + case 9: return &op_th_lwib; + case 10: return &op_th_lurw; + case 11: return &op_th_lwia; + case 12: return &op_th_lrd; + case 13: return &op_th_ldib; + case 14: return &op_th_lurd; + case 15: return &op_th_ldia; + case 16: return &op_th_lrbu; + case 17: return &op_th_lbuib; + case 18: return &op_th_lurbu; + case 19: return &op_th_lbuia; + case 20: return &op_th_lrhu; + case 21: return &op_th_lhuib; + case 22: return &op_th_lurhu; + case 23: return &op_th_lhuia; + case 24: return &op_th_lrwu; + case 25: return &op_th_lwuib; + case 26: return &op_th_lurwu; + case 27: return &op_th_lwuia; } break; case 5: switch ((inst >> 27) & 0b11111) { - case 0: op = rv_op_th_srb; break; - case 1: op = rv_op_th_sbib; break; - case 2: op = rv_op_th_surb; break; - case 3: op = rv_op_th_sbia; break; - case 4: op = rv_op_th_srh; break; - case 5: op = rv_op_th_shib; break; - case 6: op = rv_op_th_surh; break; - case 7: op = rv_op_th_shia; break; - case 8: op = rv_op_th_srw; break; - case 9: op = rv_op_th_swib; break; - case 10: op = rv_op_th_surw; break; - case 11: op = rv_op_th_swia; break; - case 12: op = rv_op_th_srd; break; - case 13: op = rv_op_th_sdib; break; - case 14: op = rv_op_th_surd; break; - case 15: op = rv_op_th_sdia; break; + case 0: return &op_th_srb; + case 1: return &op_th_sbib; + case 2: return &op_th_surb; + case 3: return &op_th_sbia; + case 4: return &op_th_srh; + case 5: return &op_th_shib; + case 6: return &op_th_surh; + case 7: return &op_th_shia; + case 8: return &op_th_srw; + case 9: return &op_th_swib; + case 10: return &op_th_surw; + case 11: return &op_th_swia; + case 12: return &op_th_srd; + case 13: return &op_th_sdib; + case 14: return &op_th_surd; + case 15: return &op_th_sdia; } break; break; @@ -638,13 +395,12 @@ const rv_opcode_data *decode_xtheadmemidx(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadmempair(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -654,15 +410,15 @@ const rv_opcode_data *decode_xtheadmempair(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 4: switch ((inst >> 27) & 0b11111) { - case 28: op = rv_op_th_lwd; break; - case 30: op = rv_op_th_lwud; break; - case 31: op = rv_op_th_ldd; break; + case 28: return &op_th_lwd; + case 30: return &op_th_lwud; + case 31: return &op_th_ldd; } break; case 5: switch ((inst >> 27) & 0b11111) { - case 28: op = rv_op_th_swd; break; - case 31: op = rv_op_th_sdd; break; + case 28: return &op_th_swd; + case 31: return &op_th_sdd; } break; } @@ -672,13 +428,12 @@ const rv_opcode_data *decode_xtheadmempair(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } const rv_opcode_data *decode_xtheadsync(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch (((inst >> 0) & 0b11)) { case 3: @@ -688,13 +443,13 @@ const rv_opcode_data *decode_xtheadsync(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 25) & 0b1111111) { - case 0b0000010: op = rv_op_th_sfence_vmas; break; + case 0b0000010: return &op_th_sfence_vmas; case 0b0000000: switch ((inst >> 20) & 0b11111) { - case 0b11000: op = rv_op_th_sync; break; - case 0b11010: op = rv_op_th_sync_i; break; - case 0b11011: op = rv_op_th_sync_is; break; - case 0b11001: op = rv_op_th_sync_s; break; + case 0b11000: return &op_th_sync; + case 0b11010: return &op_th_sync_i; + case 0b11011: return &op_th_sync_is; + case 0b11001: return &op_th_sync_s; } break; } @@ -706,5 +461,5 @@ const rv_opcode_data *decode_xtheadsync(rv_decode *dec, rv_isa isa) break; } - return op == rv_op_illegal ? NULL : &xthead_opcode_data[op]; + return NULL; } From 30bb96e570274aaa7aec6e4174ff686eee623692 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:34 -0700 Subject: [PATCH 792/860] disas/riscv: Move rv_op_illegal to riscv.c illegal is no longer used in other files. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-45-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 2 +- disas/riscv.h | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 787340e811..c3a8cde37d 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -29,7 +29,7 @@ #include "disas/riscv-xlrbr.h" typedef enum { - /* 0 is reserved for rv_op_illegal. */ + rv_op_illegal = 0, rv_op_lui = 1, rv_op_auipc = 2, rv_op_jal = 3, diff --git a/disas/riscv.h b/disas/riscv.h index 241dd47d7f..5ac8c41616 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -200,10 +200,6 @@ typedef struct { uint8_t rlist; } rv_decode; -enum { - rv_op_illegal = 0 -}; - /* instruction formats */ #define rv_fmt_none "O\t" From a722ea32faf045f93a4dbc79f0e8f0bc0ef4b043 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:35 -0700 Subject: [PATCH 793/860] disas/riscv: Split out riscv-op.c.inc Move rvi_opcode_data to riscv-op.c.inc and massage the lines into OP() form. Unlike other files, keep the enumeration and the table intact for now, but build them both from the same source. Adjust the names of rv_*mop to include _op_ to match the general pattern. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-46-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 958 +++++++++++++++++++++ disas/riscv.c | 1931 +----------------------------------------- 2 files changed, 967 insertions(+), 1922 deletions(-) create mode 100644 disas/riscv-op.c.inc diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc new file mode 100644 index 0000000000..3fe934688e --- /dev/null +++ b/disas/riscv-op.c.inc @@ -0,0 +1,958 @@ +OP(illegal, "illegal", rv_codec_none, rv_fmt_none) +OP(lui, "lui", rv_codec_u, rv_fmt_rd_uimm) +OP(auipc, "auipc", rv_codec_u, rv_fmt_rd_uoffset) +OP(jal, "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal) +OP(jalr, "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr) +OP(beq, "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq) +OP(bne, "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne) +OP(blt, "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt) +OP(bge, "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge) +OP(bltu, "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset) +OP(bgeu, "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset) +OP(lb, "lb", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lh, "lh", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lw, "lw", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lbu, "lbu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lhu, "lhu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(sb, "sb", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sh, "sh", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sw, "sw", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(addi, "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi) +OP(slti, "slti", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(sltiu, "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu) +OP(xori, "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori) +OP(ori, "ori", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(andi, "andi", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(slli, "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(srli, "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(srai, "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(add, "add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sub, "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub) +OP(sll, "sll", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slt, "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt) +OP(sltu, "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu) +OP(xor, "xor", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srl, "srl", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sra, "sra", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(or, "or", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(and, "and", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(fence, "fence", rv_codec_r_f, rv_fmt_pred_succ) +OP(fence_i, "fence.i", rv_codec_none, rv_fmt_none) +OP(lwu, "lwu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(ld, "ld", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(sd, "sd", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(addiw, "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw) +OP(slliw, "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(srliw, "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(sraiw, "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(addw, "addw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(subw, "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw) +OP(sllw, "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srlw, "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sraw, "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ldu, "ldu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lq, "lq", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(sq, "sq", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(addid, "addid", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(sllid, "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(srlid, "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(sraid, "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(addd, "addd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(subd, "subd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slld, "slld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srld, "srld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srad, "srad", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mul, "mul", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulh, "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulhsu, "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulhu, "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(div, "div", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divu, "divu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rem, "rem", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remu, "remu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mulw, "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divw, "divw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divuw, "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remw, "remw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remuw, "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(muld, "muld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divd, "divd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divud, "divud", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remd, "remd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remud, "remud", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(lr_w, "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(sc_w, "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_w, "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_w, "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_w, "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_w, "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_w, "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_w, "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_w, "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_w, "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_w, "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(lr_d, "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(sc_d, "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_d, "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_d, "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_d, "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_d, "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_d, "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_d, "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_d, "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_d, "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_d, "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(lr_q, "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(sc_q, "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_q, "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_q, "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_q, "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_q, "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_q, "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_q, "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_q, "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_q, "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_q, "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(ecall, "ecall", rv_codec_none, rv_fmt_none) +OP(ebreak, "ebreak", rv_codec_none, rv_fmt_none) +OP(uret, "uret", rv_codec_none, rv_fmt_none) +OP(sret, "sret", rv_codec_none, rv_fmt_none) +OP(hret, "hret", rv_codec_none, rv_fmt_none) +OP(mret, "mret", rv_codec_none, rv_fmt_none) +OP(dret, "dret", rv_codec_none, rv_fmt_none) +OP(sfence_vm, "sfence.vm", rv_codec_r, rv_fmt_rs1) +OP(sfence_vma, "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2) +OP(wfi, "wfi", rv_codec_none, rv_fmt_none) +OP(csrrw, "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrs, "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrc, "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrwi, "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(csrrsi, "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(csrrci, "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(flw, "flw", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fsw, "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fmadd_s, "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmsub_s, "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_s, "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmadd_s, "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fadd_s, "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsub_s, "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmul_s, "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fdiv_s, "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsgnj_s, "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s) +OP(fsgnjn_s, "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s) +OP(fsgnjx_s, "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s) +OP(fmin_s, "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmax_s, "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fsqrt_s, "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fle_s, "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flt_s, "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(feq_s, "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fcvt_w_s, "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_s, "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_s_w, "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_wu, "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fmv_x_s, "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1) +OP(fclass_s, "fclass.s", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_s_x, "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fcvt_l_s, "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_s, "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_s_l, "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_lu, "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fld, "fld", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fsd, "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fmadd_d, "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmsub_d, "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_d, "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmadd_d, "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fadd_d, "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsub_d, "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmul_d, "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fdiv_d, "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsgnj_d, "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d) +OP(fsgnjn_d, "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d) +OP(fsgnjx_d, "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d) +OP(fmin_d, "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmax_d, "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fcvt_s_d, "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_s, "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsqrt_d, "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fle_d, "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flt_d, "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(feq_d, "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fcvt_w_d, "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_d, "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_d_w, "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_wu, "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fclass_d, "fclass.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fcvt_l_d, "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_d, "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fmv_x_d, "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fcvt_d_l, "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_lu, "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fmv_d_x, "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1) +OP(flq, "flq", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fsq, "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fmadd_q, "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmsub_q, "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_q, "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmadd_q, "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fadd_q, "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsub_q, "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmul_q, "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fdiv_q, "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsgnj_q, "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q) +OP(fsgnjn_q, "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q) +OP(fsgnjx_q, "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q) +OP(fmin_q, "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmax_q, "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fcvt_s_q, "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_q_s, "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_q, "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_q_d, "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsqrt_q, "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fle_q, "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flt_q, "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(feq_q, "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fcvt_w_q, "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_q, "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_q_w, "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_wu, "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fclass_q, "fclass.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fcvt_l_q, "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_q, "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_q_l, "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_lu, "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fmv_x_q, "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_q_x, "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1) +OP(c_addi4spn, "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(rv_op_addi)) +OP(c_fld, "c.fld", rv_codec_cl_ld, NULL, DECOMP(rv_op_fld)) +OP(c_lw, "c.lw", rv_codec_cl_lw, NULL, DECOMP(rv_op_lw)) +OP(c_flw, "c.flw", rv_codec_cl_lw, NULL, DECOMP(rv_op_flw)) +OP(c_fsd, "c.fsd", rv_codec_cs_sd, NULL, DECOMP(rv_op_fsd)) +OP(c_sw, "c.sw", rv_codec_cs_sw, NULL, DECOMP(rv_op_sw)) +OP(c_fsw, "c.fsw", rv_codec_cs_sw, NULL, DECOMP(rv_op_fsw)) +OP(c_addi, "c.addi", rv_codec_ci, NULL, DECOMP(rv_op_addi)) +OP(c_jal, "c.jal", rv_codec_cj_jal, NULL, DECOMP(rv_op_jal)) +OP(c_li, "c.li", rv_codec_ci_li, NULL, DECOMP(rv_op_addi)) +OP(c_addi16sp, "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(rv_op_addi)) +OP(c_lui, "c.lui", rv_codec_ci_lui, NULL, DECOMP(rv_op_lui)) +OP(c_srli, "c.srli", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srli)) +OP(c_srai, "c.srai", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srai)) +OP(c_andi, "c.andi", rv_codec_cb_imm, NULL, DECOMP(rv_op_andi)) +OP(c_sub, "c.sub", rv_codec_cs, NULL, DECOMP(rv_op_sub)) +OP(c_xor, "c.xor", rv_codec_cs, NULL, DECOMP(rv_op_xor)) +OP(c_or, "c.or", rv_codec_cs, NULL, DECOMP(rv_op_or)) +OP(c_and, "c.and", rv_codec_cs, NULL, DECOMP(rv_op_and)) +OP(c_subw, "c.subw", rv_codec_cs, NULL, DECOMP(rv_op_subw)) +OP(c_addw, "c.addw", rv_codec_cs, NULL, DECOMP(rv_op_addw)) +OP(c_j, "c.j", rv_codec_cj, NULL, DECOMP(rv_op_j)) +OP(c_beqz, "c.beqz", rv_codec_cb, NULL, DECOMP(rv_op_beqz)) +OP(c_bnez, "c.bnez", rv_codec_cb, NULL, DECOMP(rv_op_bnez)) +OP(c_slli, "c.slli", rv_codec_ci_sh6, NULL, DECOMP(rv_op_slli)) +OP(c_fldsp, "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_fld)) +OP(c_lwsp, "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_lw)) +OP(c_flwsp, "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_flw)) +OP(c_jr, "c.jr", rv_codec_cr_jr, NULL, DECOMP(rv_op_jr)) +OP(c_mv, "c.mv", rv_codec_cr_mv, NULL, DECOMP(rv_op_mv)) +OP(c_ebreak, "c.ebreak", rv_codec_ci_none, NULL, DECOMP(rv_op_ebreak)) +OP(c_jalr, "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(rv_op_jalr)) +OP(c_add, "c.add", rv_codec_cr, NULL, DECOMP(rv_op_add)) +OP(c_fsdsp, "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_fsd)) +OP(c_swsp, "c.swsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_sw)) +OP(c_fswsp, "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_fsw)) +OP(c_ld, "c.ld", rv_codec_cl_ld, NULL, DECOMP(rv_op_ld)) +OP(c_sd, "c.sd", rv_codec_cs_sd, NULL, DECOMP(rv_op_sd)) +OP(c_addiw, "c.addiw", rv_codec_ci, NULL, DECOMP(rv_op_addiw)) +OP(c_ldsp, "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_ld)) +OP(c_sdsp, "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_sd)) +OP(c_lq, "c.lq", rv_codec_cl_lq, NULL, DECOMP(rv_op_lq)) +OP(c_sq, "c.sq", rv_codec_cs_sq, NULL, DECOMP(rv_op_sq)) +OP(c_lqsp, "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(rv_op_lq)) +OP(c_sqsp, "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(rv_op_sq)) +OP(nop, "nop", rv_codec_illegal, rv_fmt_none) +OP(mv, "mv", rv_codec_illegal, rv_fmt_rd_rs1, rvcp_mv) +OP(not, "not", rv_codec_illegal, rv_fmt_rd_rs1) +OP(neg, "neg", rv_codec_illegal, rv_fmt_rd_rs2) +OP(negw, "negw", rv_codec_illegal, rv_fmt_rd_rs2) +OP(sext_w, "sext.w", rv_codec_illegal, rv_fmt_rd_rs1) +OP(seqz, "seqz", rv_codec_illegal, rv_fmt_rd_rs1) +OP(snez, "snez", rv_codec_illegal, rv_fmt_rd_rs2) +OP(sltz, "sltz", rv_codec_illegal, rv_fmt_rd_rs1) +OP(sgtz, "sgtz", rv_codec_illegal, rv_fmt_rd_rs2) +OP(fmv_s, "fmv.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fabs_s, "fabs.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fneg_s, "fneg.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_d, "fmv.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fabs_d, "fabs.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fneg_d, "fneg.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_q, "fmv.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fabs_q, "fabs.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fneg_q, "fneg.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(beqz, "beqz", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bnez, "bnez", rv_codec_illegal, rv_fmt_rs1_offset) +OP(blez, "blez", rv_codec_illegal, rv_fmt_rs2_offset) +OP(bgez, "bgez", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bltz, "bltz", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bgtz, "bgtz", rv_codec_illegal, rv_fmt_rs2_offset) +OP(jal_ra, "jal", rv_codec_illegal, rv_fmt_offset) +OP(jalr_ra, "jalr", rv_codec_illegal, rv_fmt_rs1) +OP(j, "j", rv_codec_illegal, rv_fmt_offset) +OP(ret, "ret", rv_codec_illegal, rv_fmt_none) +OP(jr, "jr", rv_codec_illegal, rv_fmt_rs1, rvcp_jr) +OP(rdcycle, "rdcycle", rv_codec_i_csr, rv_fmt_rd) +OP(rdtime, "rdtime", rv_codec_i_csr, rv_fmt_rd) +OP(rdinstret, "rdinstret", rv_codec_i_csr, rv_fmt_rd) +OP(rdcycleh, "rdcycleh", rv_codec_i_csr, rv_fmt_rd) +OP(rdtimeh, "rdtimeh", rv_codec_i_csr, rv_fmt_rd) +OP(rdinstreth, "rdinstreth", rv_codec_i_csr, rv_fmt_rd) +OP(frcsr, "frcsr", rv_codec_i_csr, rv_fmt_rd) +OP(frrm, "frrm", rv_codec_i_csr, rv_fmt_rd) +OP(frflags, "frflags", rv_codec_i_csr, rv_fmt_rd) +OP(fscsr, "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsrm, "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsflags, "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsrmi, "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm) +OP(fsflagsi, "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm) +OP(bseti, "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(bclri, "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(binvi, "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(bexti, "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(rori, "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(clz, "clz", rv_codec_r, rv_fmt_rd_rs1) +OP(ctz, "ctz", rv_codec_r, rv_fmt_rd_rs1) +OP(cpop, "cpop", rv_codec_r, rv_fmt_rd_rs1) +OP(sext_h, "sext.h", rv_codec_r, rv_fmt_rd_rs1) +OP(sext_b, "sext.b", rv_codec_r, rv_fmt_rd_rs1) +OP(xnor, "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(orn, "orn", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(andn, "andn", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rol, "rol", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ror, "ror", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh1add, "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh2add, "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh3add, "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh1add_uw, "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh2add_uw, "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh3add_uw, "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clmul, "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clmulr, "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clmulh, "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(min, "min", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(minu, "minu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(max, "max", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(maxu, "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clzw, "clzw", rv_codec_r, rv_fmt_rd_rs1) +OP(ctzw, "ctzw", rv_codec_r, rv_fmt_rd_rs1) +OP(cpopw, "cpopw", rv_codec_r, rv_fmt_rd_rs1) +OP(slli_uw, "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(add_uw, "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rolw, "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rorw, "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rev8, "rev8", rv_codec_r, rv_fmt_rd_rs1) +OP(zext_h, "zext.h", rv_codec_r, rv_fmt_rd_rs1) +OP(roriw, "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(orc_b, "orc.b", rv_codec_r, rv_fmt_rd_rs1) +OP(bset, "bset", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bclr, "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(binv, "binv", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bext, "bext", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes32esmi, "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32esi, "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32dsmi, "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32dsi, "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes64ks1i, "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum) +OP(aes64ks2, "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64im, "aes64im", rv_codec_r, rv_fmt_rd_rs1) +OP(aes64esm, "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64es, "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64dsm, "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64ds, "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha256sig0, "sha256sig0", rv_codec_r, rv_fmt_rd_rs1) +OP(sha256sig1, "sha256sig1", rv_codec_r, rv_fmt_rd_rs1) +OP(sha256sum0, "sha256sum0", rv_codec_r, rv_fmt_rd_rs1) +OP(sha256sum1, "sha256sum1", rv_codec_r, rv_fmt_rd_rs1) +OP(sha512sig0, "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1, "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum0, "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum1, "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum0r, "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum1r, "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig0l, "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig0h, "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1l, "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1h, "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sm3p0, "sm3p0", rv_codec_r, rv_fmt_rd_rs1) +OP(sm3p1, "sm3p1", rv_codec_r, rv_fmt_rd_rs1) +OP(sm4ed, "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(sm4ks, "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(brev8, "brev8", rv_codec_r, rv_fmt_rd_rs1) +OP(pack, "pack", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(packh, "packh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(packw, "packw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(unzip, "unzip", rv_codec_r, rv_fmt_rd_rs1) +OP(zip, "zip", rv_codec_r, rv_fmt_rd_rs1) +OP(xperm4, "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(xperm8, "xperm8", rv_codec_r, rv_fmt_rd_rs1) +OP(vle8_v, "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle16_v, "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle32_v, "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle64_v, "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse8_v, "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse16_v, "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse32_v, "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse64_v, "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vlm_v, "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vsm_v, "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vlse8_v, "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse16_v, "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse32_v, "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse64_v, "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse8_v, "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse16_v, "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse32_v, "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse64_v, "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vluxei8_v, "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei16_v, "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei32_v, "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei64_v, "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei8_v, "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei16_v, "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei32_v, "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei64_v, "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei8_v, "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei16_v, "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei32_v, "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei64_v, "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei8_v, "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei16_v, "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei32_v, "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei64_v, "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vle8ff_v, "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle16ff_v, "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle32ff_v, "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle64ff_v, "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re8_v, "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re16_v, "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re32_v, "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re64_v, "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re8_v, "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re16_v, "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re32_v, "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re64_v, "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re8_v, "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re16_v, "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re32_v, "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re64_v, "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re8_v, "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re16_v, "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re32_v, "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re64_v, "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs1r_v, "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs2r_v, "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs4r_v, "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs8r_v, "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vadd_vv, "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vadd_vx, "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vadd_vi, "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vsub_vv, "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsub_vx, "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrsub_vx, "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrsub_vi, "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vwaddu_vv, "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwaddu_vx, "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwadd_vv, "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwadd_vx, "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsubu_vv, "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsubu_vx, "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsub_vv, "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsub_vx, "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwaddu_wv, "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwaddu_wx, "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwadd_wv, "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwadd_wx, "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsubu_wv, "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsubu_wx, "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsub_wv, "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsub_wx, "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vadc_vvm, "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vadc_vxm, "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vadc_vim, "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vmadc_vvm, "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmadc_vxm, "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmadc_vim, "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vsbc_vvm, "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vsbc_vxm, "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmsbc_vvm, "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmsbc_vxm, "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vand_vv, "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vand_vx, "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vand_vi, "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vor_vv, "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vor_vx, "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vor_vi, "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vxor_vv, "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vxor_vx, "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vxor_vi, "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vsll_vv, "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsll_vx, "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsll_vi, "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsrl_vv, "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsrl_vx, "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsrl_vi, "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsra_vv, "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsra_vx, "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsra_vi, "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnsrl_wv, "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnsrl_wx, "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnsrl_wi, "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnsra_wv, "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnsra_wx, "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnsra_wi, "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vmseq_vv, "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmseq_vx, "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmseq_vi, "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsne_vv, "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsne_vx, "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsne_vi, "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsltu_vv, "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsltu_vx, "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmslt_vv, "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmslt_vx, "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsleu_vv, "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsleu_vx, "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsleu_vi, "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsle_vv, "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsle_vx, "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsle_vi, "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsgtu_vx, "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsgtu_vi, "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsgt_vx, "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsgt_vi, "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vminu_vv, "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vminu_vx, "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmin_vv, "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmin_vx, "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmaxu_vv, "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmaxu_vx, "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmax_vv, "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmax_vx, "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmul_vv, "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmul_vx, "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulh_vv, "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulh_vx, "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulhu_vv, "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulhu_vx, "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulhsu_vv, "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulhsu_vx, "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vdivu_vv, "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vdivu_vx, "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vdiv_vv, "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vdiv_vx, "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vremu_vv, "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vremu_vx, "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrem_vv, "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrem_vx, "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmulu_vv, "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmulu_vx, "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmulsu_vv, "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmulsu_vx, "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmul_vv, "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmul_vx, "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmacc_vv, "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vmacc_vx, "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vnmsac_vv, "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vnmsac_vx, "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vmadd_vv, "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vmadd_vx, "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vnmsub_vv, "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vnmsub_vx, "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccu_vv, "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmaccu_vx, "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmacc_vv, "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmacc_vx, "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccsu_vv, "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmaccsu_vx, "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccus_vx, "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vmv_v_v, "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1) +OP(vmv_v_x, "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1) +OP(vmv_v_i, "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm) +OP(vmerge_vvm, "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmerge_vxm, "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmerge_vim, "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vsaddu_vv, "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsaddu_vx, "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsaddu_vi, "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vsadd_vv, "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsadd_vx, "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsadd_vi, "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vssubu_vv, "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssubu_vx, "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssub_vv, "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssub_vx, "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vaadd_vv, "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vaadd_vx, "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vaaddu_vv, "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vaaddu_vx, "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vasub_vv, "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vasub_vx, "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vasubu_vv, "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vasubu_vx, "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsmul_vv, "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsmul_vx, "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssrl_vv, "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssrl_vx, "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssrl_vi, "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vssra_vv, "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssra_vx, "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssra_vi, "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnclipu_wv, "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclipu_wx, "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnclipu_wi, "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnclip_wv, "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclip_wx, "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnclip_wi, "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vfadd_vv, "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfadd_vf, "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsub_vv, "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsub_vf, "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfrsub_vf, "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwadd_vv, "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwadd_vf, "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwadd_wv, "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwadd_wf, "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwsub_vv, "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwsub_vf, "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwsub_wv, "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwsub_wf, "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmul_vv, "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmul_vf, "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfdiv_vv, "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfdiv_vf, "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfrdiv_vf, "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwmul_vv, "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwmul_vf, "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmacc_vv, "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmacc_vf, "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmacc_vv, "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmacc_vf, "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmsac_vv, "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmsac_vf, "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmsac_vv, "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmsac_vf, "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmadd_vv, "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmadd_vf, "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmadd_vv, "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmadd_vf, "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmsub_vv, "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmsub_vf, "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmsub_vv, "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmsub_vf, "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmacc_vv, "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmacc_vf, "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwnmacc_vv, "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwnmacc_vf, "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmsac_vv, "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmsac_vf, "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwnmsac_vv, "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwnmsac_vf, "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfsqrt_v, "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfrsqrt7_v, "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfrec7_v, "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfmin_vv, "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmin_vf, "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmax_vv, "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmax_vf, "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnj_vv, "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsgnj_vf, "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnjn_vv, "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsgnjn_vf, "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnjx_vv, "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsgnjx_vf, "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfslide1up_vf, "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfslide1down_vf, "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfeq_vv, "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmfeq_vf, "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfne_vv, "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmfne_vf, "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmflt_vv, "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmflt_vf, "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfle_vv, "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmfle_vf, "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfgt_vf, "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfge_vf, "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfclass_v, "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfmerge_vfm, "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl) +OP(vfmv_v_f, "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1) +OP(vfcvt_xu_f_v, "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_x_f_v, "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_f_xu_v, "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_f_x_v, "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_rtz_xu_f_v, "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_rtz_x_f_v, "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_xu_f_v, "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_x_f_v, "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_f_xu_v, "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_f_x_v, "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_f_f_v, "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_rtz_xu_f_v, "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_rtz_x_f_v, "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_xu_f_w, "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_x_f_w, "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_f_xu_w, "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_f_x_w, "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_f_f_w, "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rod_f_f_w, "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rtz_xu_f_w, "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rtz_x_f_w, "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vredsum_vs, "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredand_vs, "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredor_vs, "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredxor_vs, "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredminu_vs, "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmin_vs, "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmaxu_vs, "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmax_vs, "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwredsumu_vs, "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwredsum_vs, "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredusum_vs, "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredosum_vs, "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredmin_vs, "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredmax_vs, "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwredusum_vs, "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwredosum_vs, "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmand_mm, "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmnand_mm, "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmandn_mm, "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmxor_mm, "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmor_mm, "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmnor_mm, "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmorn_mm, "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmxnor_mm, "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vcpop_m, "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) +OP(vfirst_m, "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) +OP(vmsbf_m, "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmsif_m, "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmsof_m, "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(viota_m, "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vid_v, "vid.v", rv_codec_v_r, rv_fmt_vd_vm) +OP(vmv_x_s, "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2) +OP(vmv_s_x, "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1) +OP(vfmv_f_s, "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2) +OP(vfmv_s_f, "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1) +OP(vslideup_vx, "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslideup_vi, "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vslide1up_vx, "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslidedown_vx, "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslidedown_vi, "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vslide1down_vx, "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrgather_vv, "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrgatherei16_vv, "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrgather_vx, "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrgather_vi, "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vcompress_vm, "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vmv1r_v, "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv2r_v, "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv4r_v, "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv8r_v, "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vzext_vf2, "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vzext_vf4, "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vzext_vf8, "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsext_vf2, "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsext_vf4, "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsext_vf8, "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsetvli, "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli) +OP(vsetivli, "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli) +OP(vsetvl, "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(c_zext_b, "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_sext_b, "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_zext_h, "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_sext_h, "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_zext_w, "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_not, "c.not", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_mul, "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2) +OP(c_lbu, "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) +OP(c_lhu, "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_lh, "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_sb, "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) +OP(c_sh, "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(cm_push, "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist) +OP(cm_pop, "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_popret, "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_popretz, "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_mva01s, "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) +OP(cm_mvsa01, "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) +OP(cm_jt, "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) +OP(cm_jalt, "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) +OP(czero_eqz, "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(czero_nez, "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(fcvt_bf16_s, "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_s_bf16, "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(vfncvtbf16_f_f_w, "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvtbf16_f_f_v, "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwmaccbf16_vv, "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmaccbf16_vf, "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(flh, "flh", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fsh, "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fmv_h_x, "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_x_h, "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1) +OP(fli_s, "fli.s", rv_codec_fli, rv_fmt_fli) +OP(fli_d, "fli.d", rv_codec_fli, rv_fmt_fli) +OP(fli_q, "fli.q", rv_codec_fli, rv_fmt_fli) +OP(fli_h, "fli.h", rv_codec_fli, rv_fmt_fli) +OP(fminm_s, "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_s, "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_d, "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_d, "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_q, "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_q, "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_h, "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_h, "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fround_s, "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_s, "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_d, "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_d, "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_q, "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_q, "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_h, "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_h, "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvtmod_w_d, "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fmvh_x_d, "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvp_d_x, "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2) +OP(fmvh_x_q, "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvp_q_x, "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2) +OP(fleq_s, "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_s, "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_d, "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_d, "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_q, "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_q, "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_h, "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_h, "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(vaesdf_vv, "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdf_vs, "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdm_vv, "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdm_vs, "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesef_vv, "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesef_vs, "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesem_vv, "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesem_vs, "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaeskf1_vi, "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vaeskf2_vi, "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vaesz_vs, "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vandn_vv, "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vandn_vx, "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vbrev_v, "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vbrev8_v, "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vclmul_vv, "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vclmul_vx, "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vclmulh_vv, "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vclmulh_vx, "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vclz_v, "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vcpop_v, "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vctz_v, "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vghsh_vv, "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vgmul_vv, "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vrev8_v, "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vrol_vv, "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrol_vx, "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vror_vv, "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vror_vx, "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vror_vi, "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm) +OP(vsha2ch_vv, "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vsha2cl_vv, "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vsha2ms_vv, "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vsm3c_vi, "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vsm3me_vv, "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vsm4k_vi, "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vsm4r_vv, "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vsm4r_vs, "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vwsll_vv, "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsll_vx, "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsll_vi, "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(amocas_w, "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_d, "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_q, "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(mop_r_0, "mop.r.0", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_1, "mop.r.1", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_2, "mop.r.2", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_3, "mop.r.3", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_4, "mop.r.4", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_5, "mop.r.5", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_6, "mop.r.6", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_7, "mop.r.7", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_8, "mop.r.8", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_9, "mop.r.9", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_10, "mop.r.10", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_11, "mop.r.11", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_12, "mop.r.12", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_13, "mop.r.13", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_14, "mop.r.14", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_15, "mop.r.15", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_16, "mop.r.16", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_17, "mop.r.17", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_18, "mop.r.18", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_19, "mop.r.19", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_20, "mop.r.20", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_21, "mop.r.21", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_22, "mop.r.22", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_23, "mop.r.23", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_24, "mop.r.24", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_25, "mop.r.25", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_26, "mop.r.26", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_27, "mop.r.27", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_28, "mop.r.28", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_29, "mop.r.29", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_30, "mop.r.30", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r_31, "mop.r.31", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_rr_0, "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr_1, "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr_2, "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr_3, "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr_4, "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr_5, "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr_6, "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr_7, "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(c_mop_1, "c.mop.1", rv_codec_ci_none, rv_fmt_none) +OP(c_mop_3, "c.mop.3", rv_codec_ci_none, rv_fmt_none) +OP(c_mop_5, "c.mop.5", rv_codec_ci_none, rv_fmt_none) +OP(c_mop_7, "c.mop.7", rv_codec_ci_none, rv_fmt_none) +OP(c_mop_9, "c.mop.9", rv_codec_ci_none, rv_fmt_none) +OP(c_mop_11, "c.mop.11", rv_codec_ci_none, rv_fmt_none) +OP(c_mop_13, "c.mop.13", rv_codec_ci_none, rv_fmt_none) +OP(c_mop_15, "c.mop.15", rv_codec_ci_none, rv_fmt_none) +OP(amoswap_b, "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_b, "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_b, "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_b, "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_b, "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_b, "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_b, "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_b, "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_b, "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_h, "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_h, "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_h, "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_h, "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_h, "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_h, "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_h, "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_h, "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_h, "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_b, "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_h, "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(wrs_sto, "wrs.sto", rv_codec_none, rv_fmt_none) +OP(wrs_nto, "wrs.nto", rv_codec_none, rv_fmt_none) +OP(lpad, "lpad", rv_codec_lp, rv_fmt_imm) +OP(sspush, "sspush", rv_codec_r, rv_fmt_rs2) +OP(sspopchk, "sspopchk", rv_codec_r, rv_fmt_rs1) +OP(ssrdp, "ssrdp", rv_codec_r, rv_fmt_rd) +OP(ssamoswap_w, "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(ssamoswap_d, "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(c_sspush, "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspush)) +OP(c_sspopchk, "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspopchk)) +OP(cbo_inval, "cbo.inval", rv_codec_r, rv_fmt_rs1) +OP(cbo_clean, "cbo.clean", rv_codec_r, rv_fmt_rs1) +OP(cbo_flush, "cbo.flush", rv_codec_r, rv_fmt_rs1) +OP(cbo_zero, "cbo.zero", rv_codec_r, rv_fmt_rs1) +OP(mnret, "mnret", rv_codec_none, rv_fmt_none) diff --git a/disas/riscv.c b/disas/riscv.c index c3a8cde37d..8aaad96bf4 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -29,964 +29,9 @@ #include "disas/riscv-xlrbr.h" typedef enum { - rv_op_illegal = 0, - rv_op_lui = 1, - rv_op_auipc = 2, - rv_op_jal = 3, - rv_op_jalr = 4, - rv_op_beq = 5, - rv_op_bne = 6, - rv_op_blt = 7, - rv_op_bge = 8, - rv_op_bltu = 9, - rv_op_bgeu = 10, - rv_op_lb = 11, - rv_op_lh = 12, - rv_op_lw = 13, - rv_op_lbu = 14, - rv_op_lhu = 15, - rv_op_sb = 16, - rv_op_sh = 17, - rv_op_sw = 18, - rv_op_addi = 19, - rv_op_slti = 20, - rv_op_sltiu = 21, - rv_op_xori = 22, - rv_op_ori = 23, - rv_op_andi = 24, - rv_op_slli = 25, - rv_op_srli = 26, - rv_op_srai = 27, - rv_op_add = 28, - rv_op_sub = 29, - rv_op_sll = 30, - rv_op_slt = 31, - rv_op_sltu = 32, - rv_op_xor = 33, - rv_op_srl = 34, - rv_op_sra = 35, - rv_op_or = 36, - rv_op_and = 37, - rv_op_fence = 38, - rv_op_fence_i = 39, - rv_op_lwu = 40, - rv_op_ld = 41, - rv_op_sd = 42, - rv_op_addiw = 43, - rv_op_slliw = 44, - rv_op_srliw = 45, - rv_op_sraiw = 46, - rv_op_addw = 47, - rv_op_subw = 48, - rv_op_sllw = 49, - rv_op_srlw = 50, - rv_op_sraw = 51, - rv_op_ldu = 52, - rv_op_lq = 53, - rv_op_sq = 54, - rv_op_addid = 55, - rv_op_sllid = 56, - rv_op_srlid = 57, - rv_op_sraid = 58, - rv_op_addd = 59, - rv_op_subd = 60, - rv_op_slld = 61, - rv_op_srld = 62, - rv_op_srad = 63, - rv_op_mul = 64, - rv_op_mulh = 65, - rv_op_mulhsu = 66, - rv_op_mulhu = 67, - rv_op_div = 68, - rv_op_divu = 69, - rv_op_rem = 70, - rv_op_remu = 71, - rv_op_mulw = 72, - rv_op_divw = 73, - rv_op_divuw = 74, - rv_op_remw = 75, - rv_op_remuw = 76, - rv_op_muld = 77, - rv_op_divd = 78, - rv_op_divud = 79, - rv_op_remd = 80, - rv_op_remud = 81, - rv_op_lr_w = 82, - rv_op_sc_w = 83, - rv_op_amoswap_w = 84, - rv_op_amoadd_w = 85, - rv_op_amoxor_w = 86, - rv_op_amoor_w = 87, - rv_op_amoand_w = 88, - rv_op_amomin_w = 89, - rv_op_amomax_w = 90, - rv_op_amominu_w = 91, - rv_op_amomaxu_w = 92, - rv_op_lr_d = 93, - rv_op_sc_d = 94, - rv_op_amoswap_d = 95, - rv_op_amoadd_d = 96, - rv_op_amoxor_d = 97, - rv_op_amoor_d = 98, - rv_op_amoand_d = 99, - rv_op_amomin_d = 100, - rv_op_amomax_d = 101, - rv_op_amominu_d = 102, - rv_op_amomaxu_d = 103, - rv_op_lr_q = 104, - rv_op_sc_q = 105, - rv_op_amoswap_q = 106, - rv_op_amoadd_q = 107, - rv_op_amoxor_q = 108, - rv_op_amoor_q = 109, - rv_op_amoand_q = 110, - rv_op_amomin_q = 111, - rv_op_amomax_q = 112, - rv_op_amominu_q = 113, - rv_op_amomaxu_q = 114, - rv_op_ecall = 115, - rv_op_ebreak = 116, - rv_op_uret = 117, - rv_op_sret = 118, - rv_op_hret = 119, - rv_op_mret = 120, - rv_op_dret = 121, - rv_op_sfence_vm = 122, - rv_op_sfence_vma = 123, - rv_op_wfi = 124, - rv_op_csrrw = 125, - rv_op_csrrs = 126, - rv_op_csrrc = 127, - rv_op_csrrwi = 128, - rv_op_csrrsi = 129, - rv_op_csrrci = 130, - rv_op_flw = 131, - rv_op_fsw = 132, - rv_op_fmadd_s = 133, - rv_op_fmsub_s = 134, - rv_op_fnmsub_s = 135, - rv_op_fnmadd_s = 136, - rv_op_fadd_s = 137, - rv_op_fsub_s = 138, - rv_op_fmul_s = 139, - rv_op_fdiv_s = 140, - rv_op_fsgnj_s = 141, - rv_op_fsgnjn_s = 142, - rv_op_fsgnjx_s = 143, - rv_op_fmin_s = 144, - rv_op_fmax_s = 145, - rv_op_fsqrt_s = 146, - rv_op_fle_s = 147, - rv_op_flt_s = 148, - rv_op_feq_s = 149, - rv_op_fcvt_w_s = 150, - rv_op_fcvt_wu_s = 151, - rv_op_fcvt_s_w = 152, - rv_op_fcvt_s_wu = 153, - rv_op_fmv_x_s = 154, - rv_op_fclass_s = 155, - rv_op_fmv_s_x = 156, - rv_op_fcvt_l_s = 157, - rv_op_fcvt_lu_s = 158, - rv_op_fcvt_s_l = 159, - rv_op_fcvt_s_lu = 160, - rv_op_fld = 161, - rv_op_fsd = 162, - rv_op_fmadd_d = 163, - rv_op_fmsub_d = 164, - rv_op_fnmsub_d = 165, - rv_op_fnmadd_d = 166, - rv_op_fadd_d = 167, - rv_op_fsub_d = 168, - rv_op_fmul_d = 169, - rv_op_fdiv_d = 170, - rv_op_fsgnj_d = 171, - rv_op_fsgnjn_d = 172, - rv_op_fsgnjx_d = 173, - rv_op_fmin_d = 174, - rv_op_fmax_d = 175, - rv_op_fcvt_s_d = 176, - rv_op_fcvt_d_s = 177, - rv_op_fsqrt_d = 178, - rv_op_fle_d = 179, - rv_op_flt_d = 180, - rv_op_feq_d = 181, - rv_op_fcvt_w_d = 182, - rv_op_fcvt_wu_d = 183, - rv_op_fcvt_d_w = 184, - rv_op_fcvt_d_wu = 185, - rv_op_fclass_d = 186, - rv_op_fcvt_l_d = 187, - rv_op_fcvt_lu_d = 188, - rv_op_fmv_x_d = 189, - rv_op_fcvt_d_l = 190, - rv_op_fcvt_d_lu = 191, - rv_op_fmv_d_x = 192, - rv_op_flq = 193, - rv_op_fsq = 194, - rv_op_fmadd_q = 195, - rv_op_fmsub_q = 196, - rv_op_fnmsub_q = 197, - rv_op_fnmadd_q = 198, - rv_op_fadd_q = 199, - rv_op_fsub_q = 200, - rv_op_fmul_q = 201, - rv_op_fdiv_q = 202, - rv_op_fsgnj_q = 203, - rv_op_fsgnjn_q = 204, - rv_op_fsgnjx_q = 205, - rv_op_fmin_q = 206, - rv_op_fmax_q = 207, - rv_op_fcvt_s_q = 208, - rv_op_fcvt_q_s = 209, - rv_op_fcvt_d_q = 210, - rv_op_fcvt_q_d = 211, - rv_op_fsqrt_q = 212, - rv_op_fle_q = 213, - rv_op_flt_q = 214, - rv_op_feq_q = 215, - rv_op_fcvt_w_q = 216, - rv_op_fcvt_wu_q = 217, - rv_op_fcvt_q_w = 218, - rv_op_fcvt_q_wu = 219, - rv_op_fclass_q = 220, - rv_op_fcvt_l_q = 221, - rv_op_fcvt_lu_q = 222, - rv_op_fcvt_q_l = 223, - rv_op_fcvt_q_lu = 224, - rv_op_fmv_x_q = 225, - rv_op_fmv_q_x = 226, - rv_op_c_addi4spn = 227, - rv_op_c_fld = 228, - rv_op_c_lw = 229, - rv_op_c_flw = 230, - rv_op_c_fsd = 231, - rv_op_c_sw = 232, - rv_op_c_fsw = 233, - rv_op_c_addi = 235, - rv_op_c_jal = 236, - rv_op_c_li = 237, - rv_op_c_addi16sp = 238, - rv_op_c_lui = 239, - rv_op_c_srli = 240, - rv_op_c_srai = 241, - rv_op_c_andi = 242, - rv_op_c_sub = 243, - rv_op_c_xor = 244, - rv_op_c_or = 245, - rv_op_c_and = 246, - rv_op_c_subw = 247, - rv_op_c_addw = 248, - rv_op_c_j = 249, - rv_op_c_beqz = 250, - rv_op_c_bnez = 251, - rv_op_c_slli = 252, - rv_op_c_fldsp = 253, - rv_op_c_lwsp = 254, - rv_op_c_flwsp = 255, - rv_op_c_jr = 256, - rv_op_c_mv = 257, - rv_op_c_ebreak = 258, - rv_op_c_jalr = 259, - rv_op_c_add = 260, - rv_op_c_fsdsp = 261, - rv_op_c_swsp = 262, - rv_op_c_fswsp = 263, - rv_op_c_ld = 264, - rv_op_c_sd = 265, - rv_op_c_addiw = 266, - rv_op_c_ldsp = 267, - rv_op_c_sdsp = 268, - rv_op_c_lq = 269, - rv_op_c_sq = 270, - rv_op_c_lqsp = 271, - rv_op_c_sqsp = 272, - rv_op_nop = 273, - rv_op_mv = 274, - rv_op_not = 275, - rv_op_neg = 276, - rv_op_negw = 277, - rv_op_sext_w = 278, - rv_op_seqz = 279, - rv_op_snez = 280, - rv_op_sltz = 281, - rv_op_sgtz = 282, - rv_op_fmv_s = 283, - rv_op_fabs_s = 284, - rv_op_fneg_s = 285, - rv_op_fmv_d = 286, - rv_op_fabs_d = 287, - rv_op_fneg_d = 288, - rv_op_fmv_q = 289, - rv_op_fabs_q = 290, - rv_op_fneg_q = 291, - rv_op_beqz = 292, - rv_op_bnez = 293, - rv_op_blez = 294, - rv_op_bgez = 295, - rv_op_bltz = 296, - rv_op_bgtz = 297, - rv_op_jal_ra = 298, - rv_op_jalr_ra = 299, - rv_op_j = 302, - rv_op_ret = 303, - rv_op_jr = 304, - rv_op_rdcycle = 305, - rv_op_rdtime = 306, - rv_op_rdinstret = 307, - rv_op_rdcycleh = 308, - rv_op_rdtimeh = 309, - rv_op_rdinstreth = 310, - rv_op_frcsr = 311, - rv_op_frrm = 312, - rv_op_frflags = 313, - rv_op_fscsr = 314, - rv_op_fsrm = 315, - rv_op_fsflags = 316, - rv_op_fsrmi = 317, - rv_op_fsflagsi = 318, - rv_op_bseti = 319, - rv_op_bclri = 320, - rv_op_binvi = 321, - rv_op_bexti = 322, - rv_op_rori = 323, - rv_op_clz = 324, - rv_op_ctz = 325, - rv_op_cpop = 326, - rv_op_sext_h = 327, - rv_op_sext_b = 328, - rv_op_xnor = 329, - rv_op_orn = 330, - rv_op_andn = 331, - rv_op_rol = 332, - rv_op_ror = 333, - rv_op_sh1add = 334, - rv_op_sh2add = 335, - rv_op_sh3add = 336, - rv_op_sh1add_uw = 337, - rv_op_sh2add_uw = 338, - rv_op_sh3add_uw = 339, - rv_op_clmul = 340, - rv_op_clmulr = 341, - rv_op_clmulh = 342, - rv_op_min = 343, - rv_op_minu = 344, - rv_op_max = 345, - rv_op_maxu = 346, - rv_op_clzw = 347, - rv_op_ctzw = 348, - rv_op_cpopw = 349, - rv_op_slli_uw = 350, - rv_op_add_uw = 351, - rv_op_rolw = 352, - rv_op_rorw = 353, - rv_op_rev8 = 354, - rv_op_zext_h = 355, - rv_op_roriw = 356, - rv_op_orc_b = 357, - rv_op_bset = 358, - rv_op_bclr = 359, - rv_op_binv = 360, - rv_op_bext = 361, - rv_op_aes32esmi = 362, - rv_op_aes32esi = 363, - rv_op_aes32dsmi = 364, - rv_op_aes32dsi = 365, - rv_op_aes64ks1i = 366, - rv_op_aes64ks2 = 367, - rv_op_aes64im = 368, - rv_op_aes64esm = 369, - rv_op_aes64es = 370, - rv_op_aes64dsm = 371, - rv_op_aes64ds = 372, - rv_op_sha256sig0 = 373, - rv_op_sha256sig1 = 374, - rv_op_sha256sum0 = 375, - rv_op_sha256sum1 = 376, - rv_op_sha512sig0 = 377, - rv_op_sha512sig1 = 378, - rv_op_sha512sum0 = 379, - rv_op_sha512sum1 = 380, - rv_op_sha512sum0r = 381, - rv_op_sha512sum1r = 382, - rv_op_sha512sig0l = 383, - rv_op_sha512sig0h = 384, - rv_op_sha512sig1l = 385, - rv_op_sha512sig1h = 386, - rv_op_sm3p0 = 387, - rv_op_sm3p1 = 388, - rv_op_sm4ed = 389, - rv_op_sm4ks = 390, - rv_op_brev8 = 391, - rv_op_pack = 392, - rv_op_packh = 393, - rv_op_packw = 394, - rv_op_unzip = 395, - rv_op_zip = 396, - rv_op_xperm4 = 397, - rv_op_xperm8 = 398, - rv_op_vle8_v = 399, - rv_op_vle16_v = 400, - rv_op_vle32_v = 401, - rv_op_vle64_v = 402, - rv_op_vse8_v = 403, - rv_op_vse16_v = 404, - rv_op_vse32_v = 405, - rv_op_vse64_v = 406, - rv_op_vlm_v = 407, - rv_op_vsm_v = 408, - rv_op_vlse8_v = 409, - rv_op_vlse16_v = 410, - rv_op_vlse32_v = 411, - rv_op_vlse64_v = 412, - rv_op_vsse8_v = 413, - rv_op_vsse16_v = 414, - rv_op_vsse32_v = 415, - rv_op_vsse64_v = 416, - rv_op_vluxei8_v = 417, - rv_op_vluxei16_v = 418, - rv_op_vluxei32_v = 419, - rv_op_vluxei64_v = 420, - rv_op_vloxei8_v = 421, - rv_op_vloxei16_v = 422, - rv_op_vloxei32_v = 423, - rv_op_vloxei64_v = 424, - rv_op_vsuxei8_v = 425, - rv_op_vsuxei16_v = 426, - rv_op_vsuxei32_v = 427, - rv_op_vsuxei64_v = 428, - rv_op_vsoxei8_v = 429, - rv_op_vsoxei16_v = 430, - rv_op_vsoxei32_v = 431, - rv_op_vsoxei64_v = 432, - rv_op_vle8ff_v = 433, - rv_op_vle16ff_v = 434, - rv_op_vle32ff_v = 435, - rv_op_vle64ff_v = 436, - rv_op_vl1re8_v = 437, - rv_op_vl1re16_v = 438, - rv_op_vl1re32_v = 439, - rv_op_vl1re64_v = 440, - rv_op_vl2re8_v = 441, - rv_op_vl2re16_v = 442, - rv_op_vl2re32_v = 443, - rv_op_vl2re64_v = 444, - rv_op_vl4re8_v = 445, - rv_op_vl4re16_v = 446, - rv_op_vl4re32_v = 447, - rv_op_vl4re64_v = 448, - rv_op_vl8re8_v = 449, - rv_op_vl8re16_v = 450, - rv_op_vl8re32_v = 451, - rv_op_vl8re64_v = 452, - rv_op_vs1r_v = 453, - rv_op_vs2r_v = 454, - rv_op_vs4r_v = 455, - rv_op_vs8r_v = 456, - rv_op_vadd_vv = 457, - rv_op_vadd_vx = 458, - rv_op_vadd_vi = 459, - rv_op_vsub_vv = 460, - rv_op_vsub_vx = 461, - rv_op_vrsub_vx = 462, - rv_op_vrsub_vi = 463, - rv_op_vwaddu_vv = 464, - rv_op_vwaddu_vx = 465, - rv_op_vwadd_vv = 466, - rv_op_vwadd_vx = 467, - rv_op_vwsubu_vv = 468, - rv_op_vwsubu_vx = 469, - rv_op_vwsub_vv = 470, - rv_op_vwsub_vx = 471, - rv_op_vwaddu_wv = 472, - rv_op_vwaddu_wx = 473, - rv_op_vwadd_wv = 474, - rv_op_vwadd_wx = 475, - rv_op_vwsubu_wv = 476, - rv_op_vwsubu_wx = 477, - rv_op_vwsub_wv = 478, - rv_op_vwsub_wx = 479, - rv_op_vadc_vvm = 480, - rv_op_vadc_vxm = 481, - rv_op_vadc_vim = 482, - rv_op_vmadc_vvm = 483, - rv_op_vmadc_vxm = 484, - rv_op_vmadc_vim = 485, - rv_op_vsbc_vvm = 486, - rv_op_vsbc_vxm = 487, - rv_op_vmsbc_vvm = 488, - rv_op_vmsbc_vxm = 489, - rv_op_vand_vv = 490, - rv_op_vand_vx = 491, - rv_op_vand_vi = 492, - rv_op_vor_vv = 493, - rv_op_vor_vx = 494, - rv_op_vor_vi = 495, - rv_op_vxor_vv = 496, - rv_op_vxor_vx = 497, - rv_op_vxor_vi = 498, - rv_op_vsll_vv = 499, - rv_op_vsll_vx = 500, - rv_op_vsll_vi = 501, - rv_op_vsrl_vv = 502, - rv_op_vsrl_vx = 503, - rv_op_vsrl_vi = 504, - rv_op_vsra_vv = 505, - rv_op_vsra_vx = 506, - rv_op_vsra_vi = 507, - rv_op_vnsrl_wv = 508, - rv_op_vnsrl_wx = 509, - rv_op_vnsrl_wi = 510, - rv_op_vnsra_wv = 511, - rv_op_vnsra_wx = 512, - rv_op_vnsra_wi = 513, - rv_op_vmseq_vv = 514, - rv_op_vmseq_vx = 515, - rv_op_vmseq_vi = 516, - rv_op_vmsne_vv = 517, - rv_op_vmsne_vx = 518, - rv_op_vmsne_vi = 519, - rv_op_vmsltu_vv = 520, - rv_op_vmsltu_vx = 521, - rv_op_vmslt_vv = 522, - rv_op_vmslt_vx = 523, - rv_op_vmsleu_vv = 524, - rv_op_vmsleu_vx = 525, - rv_op_vmsleu_vi = 526, - rv_op_vmsle_vv = 527, - rv_op_vmsle_vx = 528, - rv_op_vmsle_vi = 529, - rv_op_vmsgtu_vx = 530, - rv_op_vmsgtu_vi = 531, - rv_op_vmsgt_vx = 532, - rv_op_vmsgt_vi = 533, - rv_op_vminu_vv = 534, - rv_op_vminu_vx = 535, - rv_op_vmin_vv = 536, - rv_op_vmin_vx = 537, - rv_op_vmaxu_vv = 538, - rv_op_vmaxu_vx = 539, - rv_op_vmax_vv = 540, - rv_op_vmax_vx = 541, - rv_op_vmul_vv = 542, - rv_op_vmul_vx = 543, - rv_op_vmulh_vv = 544, - rv_op_vmulh_vx = 545, - rv_op_vmulhu_vv = 546, - rv_op_vmulhu_vx = 547, - rv_op_vmulhsu_vv = 548, - rv_op_vmulhsu_vx = 549, - rv_op_vdivu_vv = 550, - rv_op_vdivu_vx = 551, - rv_op_vdiv_vv = 552, - rv_op_vdiv_vx = 553, - rv_op_vremu_vv = 554, - rv_op_vremu_vx = 555, - rv_op_vrem_vv = 556, - rv_op_vrem_vx = 557, - rv_op_vwmulu_vv = 558, - rv_op_vwmulu_vx = 559, - rv_op_vwmulsu_vv = 560, - rv_op_vwmulsu_vx = 561, - rv_op_vwmul_vv = 562, - rv_op_vwmul_vx = 563, - rv_op_vmacc_vv = 564, - rv_op_vmacc_vx = 565, - rv_op_vnmsac_vv = 566, - rv_op_vnmsac_vx = 567, - rv_op_vmadd_vv = 568, - rv_op_vmadd_vx = 569, - rv_op_vnmsub_vv = 570, - rv_op_vnmsub_vx = 571, - rv_op_vwmaccu_vv = 572, - rv_op_vwmaccu_vx = 573, - rv_op_vwmacc_vv = 574, - rv_op_vwmacc_vx = 575, - rv_op_vwmaccsu_vv = 576, - rv_op_vwmaccsu_vx = 577, - rv_op_vwmaccus_vx = 578, - rv_op_vmv_v_v = 579, - rv_op_vmv_v_x = 580, - rv_op_vmv_v_i = 581, - rv_op_vmerge_vvm = 582, - rv_op_vmerge_vxm = 583, - rv_op_vmerge_vim = 584, - rv_op_vsaddu_vv = 585, - rv_op_vsaddu_vx = 586, - rv_op_vsaddu_vi = 587, - rv_op_vsadd_vv = 588, - rv_op_vsadd_vx = 589, - rv_op_vsadd_vi = 590, - rv_op_vssubu_vv = 591, - rv_op_vssubu_vx = 592, - rv_op_vssub_vv = 593, - rv_op_vssub_vx = 594, - rv_op_vaadd_vv = 595, - rv_op_vaadd_vx = 596, - rv_op_vaaddu_vv = 597, - rv_op_vaaddu_vx = 598, - rv_op_vasub_vv = 599, - rv_op_vasub_vx = 600, - rv_op_vasubu_vv = 601, - rv_op_vasubu_vx = 602, - rv_op_vsmul_vv = 603, - rv_op_vsmul_vx = 604, - rv_op_vssrl_vv = 605, - rv_op_vssrl_vx = 606, - rv_op_vssrl_vi = 607, - rv_op_vssra_vv = 608, - rv_op_vssra_vx = 609, - rv_op_vssra_vi = 610, - rv_op_vnclipu_wv = 611, - rv_op_vnclipu_wx = 612, - rv_op_vnclipu_wi = 613, - rv_op_vnclip_wv = 614, - rv_op_vnclip_wx = 615, - rv_op_vnclip_wi = 616, - rv_op_vfadd_vv = 617, - rv_op_vfadd_vf = 618, - rv_op_vfsub_vv = 619, - rv_op_vfsub_vf = 620, - rv_op_vfrsub_vf = 621, - rv_op_vfwadd_vv = 622, - rv_op_vfwadd_vf = 623, - rv_op_vfwadd_wv = 624, - rv_op_vfwadd_wf = 625, - rv_op_vfwsub_vv = 626, - rv_op_vfwsub_vf = 627, - rv_op_vfwsub_wv = 628, - rv_op_vfwsub_wf = 629, - rv_op_vfmul_vv = 630, - rv_op_vfmul_vf = 631, - rv_op_vfdiv_vv = 632, - rv_op_vfdiv_vf = 633, - rv_op_vfrdiv_vf = 634, - rv_op_vfwmul_vv = 635, - rv_op_vfwmul_vf = 636, - rv_op_vfmacc_vv = 637, - rv_op_vfmacc_vf = 638, - rv_op_vfnmacc_vv = 639, - rv_op_vfnmacc_vf = 640, - rv_op_vfmsac_vv = 641, - rv_op_vfmsac_vf = 642, - rv_op_vfnmsac_vv = 643, - rv_op_vfnmsac_vf = 644, - rv_op_vfmadd_vv = 645, - rv_op_vfmadd_vf = 646, - rv_op_vfnmadd_vv = 647, - rv_op_vfnmadd_vf = 648, - rv_op_vfmsub_vv = 649, - rv_op_vfmsub_vf = 650, - rv_op_vfnmsub_vv = 651, - rv_op_vfnmsub_vf = 652, - rv_op_vfwmacc_vv = 653, - rv_op_vfwmacc_vf = 654, - rv_op_vfwnmacc_vv = 655, - rv_op_vfwnmacc_vf = 656, - rv_op_vfwmsac_vv = 657, - rv_op_vfwmsac_vf = 658, - rv_op_vfwnmsac_vv = 659, - rv_op_vfwnmsac_vf = 660, - rv_op_vfsqrt_v = 661, - rv_op_vfrsqrt7_v = 662, - rv_op_vfrec7_v = 663, - rv_op_vfmin_vv = 664, - rv_op_vfmin_vf = 665, - rv_op_vfmax_vv = 666, - rv_op_vfmax_vf = 667, - rv_op_vfsgnj_vv = 668, - rv_op_vfsgnj_vf = 669, - rv_op_vfsgnjn_vv = 670, - rv_op_vfsgnjn_vf = 671, - rv_op_vfsgnjx_vv = 672, - rv_op_vfsgnjx_vf = 673, - rv_op_vfslide1up_vf = 674, - rv_op_vfslide1down_vf = 675, - rv_op_vmfeq_vv = 676, - rv_op_vmfeq_vf = 677, - rv_op_vmfne_vv = 678, - rv_op_vmfne_vf = 679, - rv_op_vmflt_vv = 680, - rv_op_vmflt_vf = 681, - rv_op_vmfle_vv = 682, - rv_op_vmfle_vf = 683, - rv_op_vmfgt_vf = 684, - rv_op_vmfge_vf = 685, - rv_op_vfclass_v = 686, - rv_op_vfmerge_vfm = 687, - rv_op_vfmv_v_f = 688, - rv_op_vfcvt_xu_f_v = 689, - rv_op_vfcvt_x_f_v = 690, - rv_op_vfcvt_f_xu_v = 691, - rv_op_vfcvt_f_x_v = 692, - rv_op_vfcvt_rtz_xu_f_v = 693, - rv_op_vfcvt_rtz_x_f_v = 694, - rv_op_vfwcvt_xu_f_v = 695, - rv_op_vfwcvt_x_f_v = 696, - rv_op_vfwcvt_f_xu_v = 697, - rv_op_vfwcvt_f_x_v = 698, - rv_op_vfwcvt_f_f_v = 699, - rv_op_vfwcvt_rtz_xu_f_v = 700, - rv_op_vfwcvt_rtz_x_f_v = 701, - rv_op_vfncvt_xu_f_w = 702, - rv_op_vfncvt_x_f_w = 703, - rv_op_vfncvt_f_xu_w = 704, - rv_op_vfncvt_f_x_w = 705, - rv_op_vfncvt_f_f_w = 706, - rv_op_vfncvt_rod_f_f_w = 707, - rv_op_vfncvt_rtz_xu_f_w = 708, - rv_op_vfncvt_rtz_x_f_w = 709, - rv_op_vredsum_vs = 710, - rv_op_vredand_vs = 711, - rv_op_vredor_vs = 712, - rv_op_vredxor_vs = 713, - rv_op_vredminu_vs = 714, - rv_op_vredmin_vs = 715, - rv_op_vredmaxu_vs = 716, - rv_op_vredmax_vs = 717, - rv_op_vwredsumu_vs = 718, - rv_op_vwredsum_vs = 719, - rv_op_vfredusum_vs = 720, - rv_op_vfredosum_vs = 721, - rv_op_vfredmin_vs = 722, - rv_op_vfredmax_vs = 723, - rv_op_vfwredusum_vs = 724, - rv_op_vfwredosum_vs = 725, - rv_op_vmand_mm = 726, - rv_op_vmnand_mm = 727, - rv_op_vmandn_mm = 728, - rv_op_vmxor_mm = 729, - rv_op_vmor_mm = 730, - rv_op_vmnor_mm = 731, - rv_op_vmorn_mm = 732, - rv_op_vmxnor_mm = 733, - rv_op_vcpop_m = 734, - rv_op_vfirst_m = 735, - rv_op_vmsbf_m = 736, - rv_op_vmsif_m = 737, - rv_op_vmsof_m = 738, - rv_op_viota_m = 739, - rv_op_vid_v = 740, - rv_op_vmv_x_s = 741, - rv_op_vmv_s_x = 742, - rv_op_vfmv_f_s = 743, - rv_op_vfmv_s_f = 744, - rv_op_vslideup_vx = 745, - rv_op_vslideup_vi = 746, - rv_op_vslide1up_vx = 747, - rv_op_vslidedown_vx = 748, - rv_op_vslidedown_vi = 749, - rv_op_vslide1down_vx = 750, - rv_op_vrgather_vv = 751, - rv_op_vrgatherei16_vv = 752, - rv_op_vrgather_vx = 753, - rv_op_vrgather_vi = 754, - rv_op_vcompress_vm = 755, - rv_op_vmv1r_v = 756, - rv_op_vmv2r_v = 757, - rv_op_vmv4r_v = 758, - rv_op_vmv8r_v = 759, - rv_op_vzext_vf2 = 760, - rv_op_vzext_vf4 = 761, - rv_op_vzext_vf8 = 762, - rv_op_vsext_vf2 = 763, - rv_op_vsext_vf4 = 764, - rv_op_vsext_vf8 = 765, - rv_op_vsetvli = 766, - rv_op_vsetivli = 767, - rv_op_vsetvl = 768, - rv_op_c_zext_b = 769, - rv_op_c_sext_b = 770, - rv_op_c_zext_h = 771, - rv_op_c_sext_h = 772, - rv_op_c_zext_w = 773, - rv_op_c_not = 774, - rv_op_c_mul = 775, - rv_op_c_lbu = 776, - rv_op_c_lhu = 777, - rv_op_c_lh = 778, - rv_op_c_sb = 779, - rv_op_c_sh = 780, - rv_op_cm_push = 781, - rv_op_cm_pop = 782, - rv_op_cm_popret = 783, - rv_op_cm_popretz = 784, - rv_op_cm_mva01s = 785, - rv_op_cm_mvsa01 = 786, - rv_op_cm_jt = 787, - rv_op_cm_jalt = 788, - rv_op_czero_eqz = 789, - rv_op_czero_nez = 790, - rv_op_fcvt_bf16_s = 791, - rv_op_fcvt_s_bf16 = 792, - rv_op_vfncvtbf16_f_f_w = 793, - rv_op_vfwcvtbf16_f_f_v = 794, - rv_op_vfwmaccbf16_vv = 795, - rv_op_vfwmaccbf16_vf = 796, - rv_op_flh = 797, - rv_op_fsh = 798, - rv_op_fmv_h_x = 799, - rv_op_fmv_x_h = 800, - rv_op_fli_s = 801, - rv_op_fli_d = 802, - rv_op_fli_q = 803, - rv_op_fli_h = 804, - rv_op_fminm_s = 805, - rv_op_fmaxm_s = 806, - rv_op_fminm_d = 807, - rv_op_fmaxm_d = 808, - rv_op_fminm_q = 809, - rv_op_fmaxm_q = 810, - rv_op_fminm_h = 811, - rv_op_fmaxm_h = 812, - rv_op_fround_s = 813, - rv_op_froundnx_s = 814, - rv_op_fround_d = 815, - rv_op_froundnx_d = 816, - rv_op_fround_q = 817, - rv_op_froundnx_q = 818, - rv_op_fround_h = 819, - rv_op_froundnx_h = 820, - rv_op_fcvtmod_w_d = 821, - rv_op_fmvh_x_d = 822, - rv_op_fmvp_d_x = 823, - rv_op_fmvh_x_q = 824, - rv_op_fmvp_q_x = 825, - rv_op_fleq_s = 826, - rv_op_fltq_s = 827, - rv_op_fleq_d = 828, - rv_op_fltq_d = 829, - rv_op_fleq_q = 830, - rv_op_fltq_q = 831, - rv_op_fleq_h = 832, - rv_op_fltq_h = 833, - rv_op_vaesdf_vv = 834, - rv_op_vaesdf_vs = 835, - rv_op_vaesdm_vv = 836, - rv_op_vaesdm_vs = 837, - rv_op_vaesef_vv = 838, - rv_op_vaesef_vs = 839, - rv_op_vaesem_vv = 840, - rv_op_vaesem_vs = 841, - rv_op_vaeskf1_vi = 842, - rv_op_vaeskf2_vi = 843, - rv_op_vaesz_vs = 844, - rv_op_vandn_vv = 845, - rv_op_vandn_vx = 846, - rv_op_vbrev_v = 847, - rv_op_vbrev8_v = 848, - rv_op_vclmul_vv = 849, - rv_op_vclmul_vx = 850, - rv_op_vclmulh_vv = 851, - rv_op_vclmulh_vx = 852, - rv_op_vclz_v = 853, - rv_op_vcpop_v = 854, - rv_op_vctz_v = 855, - rv_op_vghsh_vv = 856, - rv_op_vgmul_vv = 857, - rv_op_vrev8_v = 858, - rv_op_vrol_vv = 859, - rv_op_vrol_vx = 860, - rv_op_vror_vv = 861, - rv_op_vror_vx = 862, - rv_op_vror_vi = 863, - rv_op_vsha2ch_vv = 864, - rv_op_vsha2cl_vv = 865, - rv_op_vsha2ms_vv = 866, - rv_op_vsm3c_vi = 867, - rv_op_vsm3me_vv = 868, - rv_op_vsm4k_vi = 869, - rv_op_vsm4r_vv = 870, - rv_op_vsm4r_vs = 871, - rv_op_vwsll_vv = 872, - rv_op_vwsll_vx = 873, - rv_op_vwsll_vi = 874, - rv_op_amocas_w = 875, - rv_op_amocas_d = 876, - rv_op_amocas_q = 877, - rv_mop_r_0 = 878, - rv_mop_r_1 = 879, - rv_mop_r_2 = 880, - rv_mop_r_3 = 881, - rv_mop_r_4 = 882, - rv_mop_r_5 = 883, - rv_mop_r_6 = 884, - rv_mop_r_7 = 885, - rv_mop_r_8 = 886, - rv_mop_r_9 = 887, - rv_mop_r_10 = 888, - rv_mop_r_11 = 889, - rv_mop_r_12 = 890, - rv_mop_r_13 = 891, - rv_mop_r_14 = 892, - rv_mop_r_15 = 893, - rv_mop_r_16 = 894, - rv_mop_r_17 = 895, - rv_mop_r_18 = 896, - rv_mop_r_19 = 897, - rv_mop_r_20 = 898, - rv_mop_r_21 = 899, - rv_mop_r_22 = 900, - rv_mop_r_23 = 901, - rv_mop_r_24 = 902, - rv_mop_r_25 = 903, - rv_mop_r_26 = 904, - rv_mop_r_27 = 905, - rv_mop_r_28 = 906, - rv_mop_r_29 = 907, - rv_mop_r_30 = 908, - rv_mop_r_31 = 909, - rv_mop_rr_0 = 910, - rv_mop_rr_1 = 911, - rv_mop_rr_2 = 912, - rv_mop_rr_3 = 913, - rv_mop_rr_4 = 914, - rv_mop_rr_5 = 915, - rv_mop_rr_6 = 916, - rv_mop_rr_7 = 917, - rv_c_mop_1 = 918, - rv_c_mop_3 = 919, - rv_c_mop_5 = 920, - rv_c_mop_7 = 921, - rv_c_mop_9 = 922, - rv_c_mop_11 = 923, - rv_c_mop_13 = 924, - rv_c_mop_15 = 925, - rv_op_amoswap_b = 926, - rv_op_amoadd_b = 927, - rv_op_amoxor_b = 928, - rv_op_amoor_b = 929, - rv_op_amoand_b = 930, - rv_op_amomin_b = 931, - rv_op_amomax_b = 932, - rv_op_amominu_b = 933, - rv_op_amomaxu_b = 934, - rv_op_amoswap_h = 935, - rv_op_amoadd_h = 936, - rv_op_amoxor_h = 937, - rv_op_amoor_h = 938, - rv_op_amoand_h = 939, - rv_op_amomin_h = 940, - rv_op_amomax_h = 941, - rv_op_amominu_h = 942, - rv_op_amomaxu_h = 943, - rv_op_amocas_b = 944, - rv_op_amocas_h = 945, - rv_op_wrs_sto = 946, - rv_op_wrs_nto = 947, - rv_op_lpad = 948, - rv_op_sspush = 949, - rv_op_sspopchk = 950, - rv_op_ssrdp = 951, - rv_op_ssamoswap_w = 952, - rv_op_ssamoswap_d = 953, - rv_op_c_sspush = 954, - rv_op_c_sspopchk = 955, - rv_op_cbo_inval = 956, - rv_op_cbo_clean = 957, - rv_op_cbo_flush = 958, - rv_op_cbo_zero = 959, - rv_op_mnret = 960, +#define OP(N, ...) rv_op_##N, +#include "riscv-op.c.inc" +#undef OP } rv_op; /* register names */ @@ -1601,967 +646,9 @@ static uint32_t operand_lpl(rv_inst inst) /* instruction metadata */ static const rv_opcode_data rvi_opcode_data[] = { - { "illegal", rv_codec_none, rv_fmt_none }, - { "lui", rv_codec_u, rv_fmt_rd_uimm }, - { "auipc", rv_codec_u, rv_fmt_rd_uoffset }, - { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal }, - { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr }, - { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq }, - { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne }, - { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt }, - { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge }, - { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset }, - { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset }, - { "lb", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "lh", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "lw", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "lbu", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "lhu", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "sb", rv_codec_s, rv_fmt_rs2_offset_rs1 }, - { "sh", rv_codec_s, rv_fmt_rs2_offset_rs1 }, - { "sw", rv_codec_s, rv_fmt_rs2_offset_rs1 }, - { "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi }, - { "slti", rv_codec_i, rv_fmt_rd_rs1_imm }, - { "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu }, - { "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori }, - { "ori", rv_codec_i, rv_fmt_rd_rs1_imm }, - { "andi", rv_codec_i, rv_fmt_rd_rs1_imm }, - { "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub }, - { "sll", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt }, - { "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu }, - { "xor", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "srl", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sra", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "or", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "and", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "fence", rv_codec_r_f, rv_fmt_pred_succ }, - { "fence.i", rv_codec_none, rv_fmt_none }, - { "lwu", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "ld", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "sd", rv_codec_s, rv_fmt_rs2_offset_rs1 }, - { "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw }, - { "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, - { "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, - { "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, - { "addw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw }, - { "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "ldu", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "lq", rv_codec_i, rv_fmt_rd_offset_rs1 }, - { "sq", rv_codec_s, rv_fmt_rs2_offset_rs1 }, - { "addid", rv_codec_i, rv_fmt_rd_rs1_imm }, - { "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, - { "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, - { "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, - { "addd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "subd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "slld", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "srld", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "srad", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mul", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "div", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "divu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "rem", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "remu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "divw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "remw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "muld", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "divd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "divud", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "remd", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "remud", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1 }, - { "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1 }, - { "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1 }, - { "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "ecall", rv_codec_none, rv_fmt_none }, - { "ebreak", rv_codec_none, rv_fmt_none }, - { "uret", rv_codec_none, rv_fmt_none }, - { "sret", rv_codec_none, rv_fmt_none }, - { "hret", rv_codec_none, rv_fmt_none }, - { "mret", rv_codec_none, rv_fmt_none }, - { "dret", rv_codec_none, rv_fmt_none }, - { "sfence.vm", rv_codec_r, rv_fmt_rs1 }, - { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2 }, - { "wfi", rv_codec_none, rv_fmt_none }, - { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, - { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, - { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1 }, - { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, - { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, - { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm }, - { "flw", rv_codec_i, rv_fmt_frd_offset_rs1 }, - { "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1 }, - { "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s }, - { "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s }, - { "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s }, - { "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1 }, - { "fclass.s", rv_codec_r, rv_fmt_rd_frs1 }, - { "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1 }, - { "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fld", rv_codec_i, rv_fmt_frd_offset_rs1 }, - { "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1 }, - { "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d }, - { "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d }, - { "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d }, - { "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fclass.d", rv_codec_r, rv_fmt_rd_frs1 }, - { "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1 }, - { "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1 }, - { "flq", rv_codec_i, rv_fmt_frd_offset_rs1 }, - { "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1 }, - { "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3 }, - { "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2 }, - { "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q }, - { "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q }, - { "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q }, - { "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fclass.q", rv_codec_r, rv_fmt_rd_frs1 }, - { "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1 }, - { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1 }, - { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1 }, - { "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(rv_op_addi) }, - { "c.fld", rv_codec_cl_ld, NULL, DECOMP(rv_op_fld) }, - { "c.lw", rv_codec_cl_lw, NULL, DECOMP(rv_op_lw) }, - { "c.flw", rv_codec_cl_lw, NULL, DECOMP(rv_op_flw) }, - { "c.fsd", rv_codec_cs_sd, NULL, DECOMP(rv_op_fsd) }, - { "c.sw", rv_codec_cs_sw, NULL, DECOMP(rv_op_sw) }, - { "c.fsw", rv_codec_cs_sw, NULL, DECOMP(rv_op_fsw) }, - { }, - { "c.addi", rv_codec_ci, NULL, DECOMP(rv_op_addi) }, - { "c.jal", rv_codec_cj_jal, NULL, DECOMP(rv_op_jal) }, - { "c.li", rv_codec_ci_li, NULL, DECOMP(rv_op_addi) }, - { "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(rv_op_addi) }, - { "c.lui", rv_codec_ci_lui, NULL, DECOMP(rv_op_lui) }, - { "c.srli", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srli) }, - { "c.srai", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srai) }, - { "c.andi", rv_codec_cb_imm, NULL, DECOMP(rv_op_andi) }, - { "c.sub", rv_codec_cs, NULL, DECOMP(rv_op_sub) }, - { "c.xor", rv_codec_cs, NULL, DECOMP(rv_op_xor) }, - { "c.or", rv_codec_cs, NULL, DECOMP(rv_op_or) }, - { "c.and", rv_codec_cs, NULL, DECOMP(rv_op_and) }, - { "c.subw", rv_codec_cs, NULL, DECOMP(rv_op_subw) }, - { "c.addw", rv_codec_cs, NULL, DECOMP(rv_op_addw) }, - { "c.j", rv_codec_cj, NULL, DECOMP(rv_op_j) }, - { "c.beqz", rv_codec_cb, NULL, DECOMP(rv_op_beqz) }, - { "c.bnez", rv_codec_cb, NULL, DECOMP(rv_op_bnez) }, - { "c.slli", rv_codec_ci_sh6, NULL, DECOMP(rv_op_slli) }, - { "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_fld) }, - { "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_lw) }, - { "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_flw) }, - { "c.jr", rv_codec_cr_jr, NULL, DECOMP(rv_op_jr) }, - { "c.mv", rv_codec_cr_mv, NULL, DECOMP(rv_op_mv) }, - { "c.ebreak", rv_codec_ci_none, NULL, DECOMP(rv_op_ebreak) }, - { "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(rv_op_jalr) }, - { "c.add", rv_codec_cr, NULL, DECOMP(rv_op_add) }, - { "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_fsd) }, - { "c.swsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_sw) }, - { "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_fsw) }, - { "c.ld", rv_codec_cl_ld, NULL, DECOMP(rv_op_ld) }, - { "c.sd", rv_codec_cs_sd, NULL, DECOMP(rv_op_sd) }, - { "c.addiw", rv_codec_ci, NULL, DECOMP(rv_op_addiw) }, - { "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_ld) }, - { "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_sd) }, - { "c.lq", rv_codec_cl_lq, NULL, DECOMP(rv_op_lq) }, - { "c.sq", rv_codec_cs_sq, NULL, DECOMP(rv_op_sq) }, - { "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(rv_op_lq) }, - { "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(rv_op_sq) }, - { "nop", rv_codec_illegal, rv_fmt_none }, - { "mv", rv_codec_illegal, rv_fmt_rd_rs1, rvcp_mv }, - { "not", rv_codec_illegal, rv_fmt_rd_rs1 }, - { "neg", rv_codec_illegal, rv_fmt_rd_rs2 }, - { "negw", rv_codec_illegal, rv_fmt_rd_rs2 }, - { "sext.w", rv_codec_illegal, rv_fmt_rd_rs1 }, - { "seqz", rv_codec_illegal, rv_fmt_rd_rs1 }, - { "snez", rv_codec_illegal, rv_fmt_rd_rs2 }, - { "sltz", rv_codec_illegal, rv_fmt_rd_rs1 }, - { "sgtz", rv_codec_illegal, rv_fmt_rd_rs2 }, - { "fmv.s", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fabs.s", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fneg.s", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fmv.d", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fabs.d", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fneg.d", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fmv.q", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fabs.q", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "fneg.q", rv_codec_illegal, rv_fmt_frd_frs1 }, - { "beqz", rv_codec_illegal, rv_fmt_rs1_offset }, - { "bnez", rv_codec_illegal, rv_fmt_rs1_offset }, - { "blez", rv_codec_illegal, rv_fmt_rs2_offset }, - { "bgez", rv_codec_illegal, rv_fmt_rs1_offset }, - { "bltz", rv_codec_illegal, rv_fmt_rs1_offset }, - { "bgtz", rv_codec_illegal, rv_fmt_rs2_offset }, - { "jal", rv_codec_illegal, rv_fmt_offset }, /* rv_op_jal_ra */ - { "jalr", rv_codec_illegal, rv_fmt_rs1 }, /* rv_op_jalr_ra */ - { }, - { }, - { "j", rv_codec_illegal, rv_fmt_offset }, - { "ret", rv_codec_illegal, rv_fmt_none }, - { "jr", rv_codec_illegal, rv_fmt_rs1, rvcp_jr }, - { "rdcycle", rv_codec_i_csr, rv_fmt_rd }, - { "rdtime", rv_codec_i_csr, rv_fmt_rd }, - { "rdinstret", rv_codec_i_csr, rv_fmt_rd }, - { "rdcycleh", rv_codec_i_csr, rv_fmt_rd }, - { "rdtimeh", rv_codec_i_csr, rv_fmt_rd }, - { "rdinstreth", rv_codec_i_csr, rv_fmt_rd }, - { "frcsr", rv_codec_i_csr, rv_fmt_rd }, - { "frrm", rv_codec_i_csr, rv_fmt_rd }, - { "frflags", rv_codec_i_csr, rv_fmt_rd }, - { "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1 }, - { "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1 }, - { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1 }, - { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm }, - { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm }, - { "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm }, - { "clz", rv_codec_r, rv_fmt_rd_rs1 }, - { "ctz", rv_codec_r, rv_fmt_rd_rs1 }, - { "cpop", rv_codec_r, rv_fmt_rd_rs1 }, - { "sext.h", rv_codec_r, rv_fmt_rd_rs1 }, - { "sext.b", rv_codec_r, rv_fmt_rd_rs1 }, - { "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "orn", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "andn", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "rol", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "ror", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "min", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "minu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "max", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "clzw", rv_codec_r, rv_fmt_rd_rs1 }, - { "ctzw", rv_codec_r, rv_fmt_rd_rs1 }, - { "cpopw", rv_codec_r, rv_fmt_rd_rs1 }, - { "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm }, - { "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "rev8", rv_codec_r, rv_fmt_rd_rs1 }, - { "zext.h", rv_codec_r, rv_fmt_rd_rs1 }, - { "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm }, - { "orc.b", rv_codec_r, rv_fmt_rd_rs1 }, - { "bset", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "binv", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "bext", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, - { "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, - { "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, - { "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, - { "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum }, - { "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "aes64im", rv_codec_r, rv_fmt_rd_rs1 }, - { "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha256sig0", rv_codec_r, rv_fmt_rd_rs1 }, - { "sha256sig1", rv_codec_r, rv_fmt_rd_rs1 }, - { "sha256sum0", rv_codec_r, rv_fmt_rd_rs1 }, - { "sha256sum1", rv_codec_r, rv_fmt_rd_rs1 }, - { "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "sm3p0", rv_codec_r, rv_fmt_rd_rs1 }, - { "sm3p1", rv_codec_r, rv_fmt_rd_rs1 }, - { "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, - { "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs }, - { "brev8", rv_codec_r, rv_fmt_rd_rs1 }, - { "pack", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "packh", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "packw", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "unzip", rv_codec_r, rv_fmt_rd_rs1 }, - { "zip", rv_codec_r, rv_fmt_rd_rs1 }, - { "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "xperm8", rv_codec_r, rv_fmt_rd_rs1 }, - { "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm }, - { "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm }, - { "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm }, - { "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, - { "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, - { "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl }, - { "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, - { "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, - { "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl }, - { "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, - { "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, - { "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, - { "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, - { "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm }, - { "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1 }, - { "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1 }, - { "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm }, - { "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl }, - { "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl }, - { "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl }, - { "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm }, - { "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm }, - { "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl }, - { "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1 }, - { "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm }, - { "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm }, - { "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vid.v", rv_codec_v_r, rv_fmt_vd_vm }, - { "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2 }, - { "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1 }, - { "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2 }, - { "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1 }, - { "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, - { "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli }, - { "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli }, - { "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd }, - { "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd }, - { "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd }, - { "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd }, - { "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd }, - { "c.not", rv_codec_zcb_ext, rv_fmt_rd }, - { "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2 }, - { "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst }, - { "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst }, - { "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst }, - { "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst }, - { "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst }, - { "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist }, - { "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist }, - { "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist }, - { "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist }, - { "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2 }, - { "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2 }, - { "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index }, - { "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index }, - { "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm }, - { "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm }, - { "flh", rv_codec_i, rv_fmt_frd_offset_rs1 }, - { "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1 }, - { "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1 }, - { "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1 }, - { "fli.s", rv_codec_fli, rv_fmt_fli }, - { "fli.d", rv_codec_fli, rv_fmt_fli }, - { "fli.q", rv_codec_fli, rv_fmt_fli }, - { "fli.h", rv_codec_fli, rv_fmt_fli }, - { "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2 }, - { "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1 }, - { "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1 }, - { "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1 }, - { "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2 }, - { "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1 }, - { "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2 }, - { "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2 }, - { "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, - { "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, - { "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, - { "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm }, - { "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm }, - { "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, - { "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, - { "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, - { "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, - { "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1 }, - { "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm }, - { "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2 }, - { "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm }, - { "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm }, - { "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm }, - { "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "mop.r.0", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.1", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.2", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.3", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.4", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.5", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.6", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.7", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.8", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.9", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.10", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.11", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.12", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.13", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.14", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.15", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.16", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.17", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.18", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.19", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.20", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.21", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.22", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.23", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.24", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.25", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.26", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.27", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.28", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.29", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.30", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.r.31", rv_codec_r, rv_fmt_rd_rs1 }, - { "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2 }, - { "c.mop.1", rv_codec_ci_none, rv_fmt_none }, - { "c.mop.3", rv_codec_ci_none, rv_fmt_none }, - { "c.mop.5", rv_codec_ci_none, rv_fmt_none }, - { "c.mop.7", rv_codec_ci_none, rv_fmt_none }, - { "c.mop.9", rv_codec_ci_none, rv_fmt_none }, - { "c.mop.11", rv_codec_ci_none, rv_fmt_none }, - { "c.mop.13", rv_codec_ci_none, rv_fmt_none }, - { "c.mop.15", rv_codec_ci_none, rv_fmt_none }, - { "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "wrs.sto", rv_codec_none, rv_fmt_none }, - { "wrs.nto", rv_codec_none, rv_fmt_none }, - { "lpad", rv_codec_lp, rv_fmt_imm }, - { "sspush", rv_codec_r, rv_fmt_rs2 }, - { "sspopchk", rv_codec_r, rv_fmt_rs1 }, - { "ssrdp", rv_codec_r, rv_fmt_rd }, - { "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1 }, - { "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspush) }, - { "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspopchk) }, - { "cbo.inval", rv_codec_r, rv_fmt_rs1 }, - { "cbo.clean", rv_codec_r, rv_fmt_rs1 }, - { "cbo.flush", rv_codec_r, rv_fmt_rs1 }, - { "cbo.zero", rv_codec_r, rv_fmt_rs1 }, - { "mnret", rv_codec_none, rv_fmt_none }, +#define OP(N, ...) { __VA_ARGS__ }, +#include "riscv-op.c.inc" +#undef OP }; /* CSR names */ @@ -2928,7 +1015,7 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) (((inst >> 11) & 0b11) == 0b0)) { unsigned int cmop_code = 0; cmop_code = ((inst >> 8) & 0b111); - op = rv_c_mop_1 + cmop_code; + op = rv_op_c_mop_1 + cmop_code; if (dec->cfg->ext_zicfiss) { op = (cmop_code == 0) ? rv_op_c_sspush : op; op = (cmop_code == 2) ? rv_op_c_sspopchk : op; @@ -4459,7 +2546,7 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) 2, 2, extract32(inst, 26, 2)), 4, 1, extract32(inst, 30, 1)); - op = rv_mop_r_0 + imm_mop5; + op = rv_op_mop_r_0 + imm_mop5; /* if zicfiss enabled and mop5 is shadow stack */ if (dec->cfg->ext_zicfiss && ((imm_mop5 & 0b11100) == 0b11100)) { @@ -4478,7 +2565,7 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) == 0b1000001) { imm_mop3 = deposit32(extract32(inst, 26, 2), 2, 1, extract32(inst, 30, 1)); - op = rv_mop_rr_0 + imm_mop3; + op = rv_op_mop_rr_0 + imm_mop3; /* if zicfiss enabled and mop3 is shadow stack */ if (dec->cfg->ext_zicfiss && ((imm_mop3 & 0b111) == 0b111)) { From 1a13fad072fb39876627b3a20b1a423a92813688 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:36 -0700 Subject: [PATCH 794/860] disas/riscv: Merge all mop.r.n to one pattern Avoid performing arithmetic on rv_op_mop_r_0. Treat the 'n' as an immediate. Create a codec and format to match. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-47-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 33 +-------------------------------- disas/riscv.c | 15 ++++++++++++++- disas/riscv.h | 2 ++ 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc index 3fe934688e..222d249a70 100644 --- a/disas/riscv-op.c.inc +++ b/disas/riscv-op.c.inc @@ -873,38 +873,7 @@ OP(vwsll_vi, "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) OP(amocas_w, "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(amocas_d, "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(amocas_q, "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(mop_r_0, "mop.r.0", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_1, "mop.r.1", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_2, "mop.r.2", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_3, "mop.r.3", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_4, "mop.r.4", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_5, "mop.r.5", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_6, "mop.r.6", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_7, "mop.r.7", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_8, "mop.r.8", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_9, "mop.r.9", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_10, "mop.r.10", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_11, "mop.r.11", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_12, "mop.r.12", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_13, "mop.r.13", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_14, "mop.r.14", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_15, "mop.r.15", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_16, "mop.r.16", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_17, "mop.r.17", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_18, "mop.r.18", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_19, "mop.r.19", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_20, "mop.r.20", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_21, "mop.r.21", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_22, "mop.r.22", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_23, "mop.r.23", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_24, "mop.r.24", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_25, "mop.r.25", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_26, "mop.r.26", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_27, "mop.r.27", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_28, "mop.r.28", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_29, "mop.r.29", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_30, "mop.r.30", rv_codec_r, rv_fmt_rd_rs1) -OP(mop_r_31, "mop.r.31", rv_codec_r, rv_fmt_rd_rs1) +OP(mop_r, "mop.r", rv_codec_mop_r, rv_fmt_mop_r) OP(mop_rr_0, "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(mop_rr_1, "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(mop_rr_2, "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2) diff --git a/disas/riscv.c b/disas/riscv.c index 8aaad96bf4..0751f1a841 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -643,6 +643,13 @@ static uint32_t operand_lpl(rv_inst inst) return extract32(inst, 12, 20); } +static uint32_t operand_mop_r_imm(rv_inst inst) +{ + return (extract32(inst, 30, 1) << 4) | + (extract32(inst, 26, 2) << 2) | + extract32(inst, 20, 2); +} + /* instruction metadata */ static const rv_opcode_data rvi_opcode_data[] = { @@ -2546,7 +2553,7 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) 2, 2, extract32(inst, 26, 2)), 4, 1, extract32(inst, 30, 1)); - op = rv_op_mop_r_0 + imm_mop5; + op = rv_op_mop_r; /* if zicfiss enabled and mop5 is shadow stack */ if (dec->cfg->ext_zicfiss && ((imm_mop5 & 0b11100) == 0b11100)) { @@ -3049,6 +3056,11 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa, dec->rs1 = dec->rs2 = operand_crs1(inst); dec->imm = 0; break; + case rv_codec_mop_r: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->imm = operand_mop_r_imm(inst); + break; default: g_assert_not_reached(); } @@ -3134,6 +3146,7 @@ static GString *format_inst(size_t tab, rv_decode *dec, g_string_append(buf, op->name); break; case '(': + case '.': case ',': case ')': case '-': diff --git a/disas/riscv.h b/disas/riscv.h index 5ac8c41616..b47a7f0150 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -160,6 +160,7 @@ typedef enum { rv_codec_fli, rv_codec_lp, rv_codec_cmop_ss, + rv_codec_mop_r, } rv_codec; /* structures */ @@ -284,5 +285,6 @@ typedef struct { #define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j" #define rv_fmt_rd2_imm "O\t0,2,(1),i" #define rv_fmt_fli "O\t3,h" +#define rv_fmt_mop_r "O.i\t0,1" #endif /* DISAS_RISCV_H */ From e2851825f44e107c1092fca916ee9800ac86380a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:37 -0700 Subject: [PATCH 795/860] disas/riscv: Merge all mop.rr.n to one pattern Avoid performing arithmetic on rv_op_mop_rr_0. Treat the 'n' as an immediate. Create a codec and format to match. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-48-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 9 +-------- disas/riscv.c | 13 ++++++++++++- disas/riscv.h | 2 ++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc index 222d249a70..2100e54475 100644 --- a/disas/riscv-op.c.inc +++ b/disas/riscv-op.c.inc @@ -874,14 +874,7 @@ OP(amocas_w, "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(amocas_d, "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(amocas_q, "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(mop_r, "mop.r", rv_codec_mop_r, rv_fmt_mop_r) -OP(mop_rr_0, "mop.rr.0", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(mop_rr_1, "mop.rr.1", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(mop_rr_2, "mop.rr.2", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(mop_rr_3, "mop.rr.3", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(mop_rr_4, "mop.rr.4", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(mop_rr_5, "mop.rr.5", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(mop_rr_6, "mop.rr.6", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(mop_rr_7, "mop.rr.7", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mop_rr, "mop.rr", rv_codec_mop_rr, rv_fmt_mop_rr) OP(c_mop_1, "c.mop.1", rv_codec_ci_none, rv_fmt_none) OP(c_mop_3, "c.mop.3", rv_codec_ci_none, rv_fmt_none) OP(c_mop_5, "c.mop.5", rv_codec_ci_none, rv_fmt_none) diff --git a/disas/riscv.c b/disas/riscv.c index 0751f1a841..22104638e1 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -650,6 +650,11 @@ static uint32_t operand_mop_r_imm(rv_inst inst) extract32(inst, 20, 2); } +static uint32_t operand_mop_rr_imm(rv_inst inst) +{ + return (extract32(inst, 30, 1) << 2) | extract32(inst, 26, 2); +} + /* instruction metadata */ static const rv_opcode_data rvi_opcode_data[] = { @@ -2572,7 +2577,7 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) == 0b1000001) { imm_mop3 = deposit32(extract32(inst, 26, 2), 2, 1, extract32(inst, 30, 1)); - op = rv_op_mop_rr_0 + imm_mop3; + op = rv_op_mop_rr; /* if zicfiss enabled and mop3 is shadow stack */ if (dec->cfg->ext_zicfiss && ((imm_mop3 & 0b111) == 0b111)) { @@ -3061,6 +3066,12 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa, dec->rs1 = operand_rs1(inst); dec->imm = operand_mop_r_imm(inst); break; + case rv_codec_mop_rr: + dec->rd = operand_rd(inst); + dec->rs1 = operand_rs1(inst); + dec->rs2 = operand_rs2(inst); + dec->imm = operand_mop_rr_imm(inst); + break; default: g_assert_not_reached(); } diff --git a/disas/riscv.h b/disas/riscv.h index b47a7f0150..47647f750f 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -161,6 +161,7 @@ typedef enum { rv_codec_lp, rv_codec_cmop_ss, rv_codec_mop_r, + rv_codec_mop_rr, } rv_codec; /* structures */ @@ -286,5 +287,6 @@ typedef struct { #define rv_fmt_rd2_imm "O\t0,2,(1),i" #define rv_fmt_fli "O\t3,h" #define rv_fmt_mop_r "O.i\t0,1" +#define rv_fmt_mop_rr "O.i\t0,1,2" #endif /* DISAS_RISCV_H */ From 59acf4d4e1a723ebb57d008cdf68a2cc8aa72a65 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:38 -0700 Subject: [PATCH 796/860] disas/riscv: Merge all c.mop.n to one pattern Avoid performing arithmetic on rv_op_c_mop_1. Treat the 'n' as an immediate. Create a codec and format to match. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-49-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 9 +-------- disas/riscv.c | 10 +++++++++- disas/riscv.h | 2 ++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc index 2100e54475..802a521bca 100644 --- a/disas/riscv-op.c.inc +++ b/disas/riscv-op.c.inc @@ -875,14 +875,7 @@ OP(amocas_d, "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(amocas_q, "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(mop_r, "mop.r", rv_codec_mop_r, rv_fmt_mop_r) OP(mop_rr, "mop.rr", rv_codec_mop_rr, rv_fmt_mop_rr) -OP(c_mop_1, "c.mop.1", rv_codec_ci_none, rv_fmt_none) -OP(c_mop_3, "c.mop.3", rv_codec_ci_none, rv_fmt_none) -OP(c_mop_5, "c.mop.5", rv_codec_ci_none, rv_fmt_none) -OP(c_mop_7, "c.mop.7", rv_codec_ci_none, rv_fmt_none) -OP(c_mop_9, "c.mop.9", rv_codec_ci_none, rv_fmt_none) -OP(c_mop_11, "c.mop.11", rv_codec_ci_none, rv_fmt_none) -OP(c_mop_13, "c.mop.13", rv_codec_ci_none, rv_fmt_none) -OP(c_mop_15, "c.mop.15", rv_codec_ci_none, rv_fmt_none) +OP(c_mop, "c.mop", rv_codec_cmop, rv_fmt_cmop) OP(amoswap_b, "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(amoadd_b, "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(amoxor_b, "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) diff --git a/disas/riscv.c b/disas/riscv.c index 22104638e1..39235e5bc7 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -643,6 +643,11 @@ static uint32_t operand_lpl(rv_inst inst) return extract32(inst, 12, 20); } +static uint32_t operand_cmop_imm(rv_inst inst) +{ + return extract32(inst, 8, 3) * 2 + 1; +} + static uint32_t operand_mop_r_imm(rv_inst inst) { return (extract32(inst, 30, 1) << 4) | @@ -1027,7 +1032,7 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) (((inst >> 11) & 0b11) == 0b0)) { unsigned int cmop_code = 0; cmop_code = ((inst >> 8) & 0b111); - op = rv_op_c_mop_1 + cmop_code; + op = rv_op_c_mop; if (dec->cfg->ext_zicfiss) { op = (cmop_code == 0) ? rv_op_c_sspush : op; op = (cmop_code == 2) ? rv_op_c_sspopchk : op; @@ -3056,6 +3061,9 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa, case rv_codec_lp: dec->imm = operand_lpl(inst); break; + case rv_codec_cmop: + dec->imm = operand_cmop_imm(inst); + break; case rv_codec_cmop_ss: dec->rd = rv_ireg_zero; dec->rs1 = dec->rs2 = operand_crs1(inst); diff --git a/disas/riscv.h b/disas/riscv.h index 47647f750f..b0269bb709 100644 --- a/disas/riscv.h +++ b/disas/riscv.h @@ -159,6 +159,7 @@ typedef enum { rv_codec_r2_imm2_imm5, rv_codec_fli, rv_codec_lp, + rv_codec_cmop, rv_codec_cmop_ss, rv_codec_mop_r, rv_codec_mop_rr, @@ -286,6 +287,7 @@ typedef struct { #define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j" #define rv_fmt_rd2_imm "O\t0,2,(1),i" #define rv_fmt_fli "O\t3,h" +#define rv_fmt_cmop "O.i" #define rv_fmt_mop_r "O.i\t0,1" #define rv_fmt_mop_rr "O.i\t0,1,2" From bf9281b1b0cddda53f00ce28082bb6e7b0d86034 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:39 -0700 Subject: [PATCH 797/860] disas/riscv: Tidy decode of c.mop.n Merge nested if's. Reuse operand_cmop_imm for zicfiss decode. Return pointers directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-50-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 39235e5bc7..197eaa43b1 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1027,18 +1027,17 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) break; case 2: op = rv_op_c_li; break; case 3: - if (dec->cfg && dec->cfg->ext_zcmop) { - if ((((inst >> 2) & 0b111111) == 0b100000) && - (((inst >> 11) & 0b11) == 0b0)) { - unsigned int cmop_code = 0; - cmop_code = ((inst >> 8) & 0b111); - op = rv_op_c_mop; - if (dec->cfg->ext_zicfiss) { - op = (cmop_code == 0) ? rv_op_c_sspush : op; - op = (cmop_code == 2) ? rv_op_c_sspopchk : op; + if (dec->cfg + && dec->cfg->ext_zcmop + && ((inst >> 2) & 0b111111) == 0b100000 + && ((inst >> 11) & 0b11) == 0) { + if (dec->cfg->ext_zicfiss) { + switch (operand_cmop_imm(inst)) { + case 1: return &rvi_opcode_data[rv_op_c_sspush]; + case 5: return &rvi_opcode_data[rv_op_c_sspopchk]; } - break; } + return &rvi_opcode_data[rv_op_c_mop]; } if (inst & ((1 << 12) | (0x1f << 2))) { switch ((inst >> 7) & 0b11111) { From ef25a26b7dc29ba4b78a70d22a6a806dd4637537 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:40 -0700 Subject: [PATCH 798/860] disas/riscv: Tidy decode of mop.r.n and mop.rr.n Merge nested if's. Reuse operand_mop_{r,rr}_imm for zicfiss decode. Return pointers directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-51-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv.c | 59 +++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/disas/riscv.c b/disas/riscv.c index 197eaa43b1..7ea65ba2e7 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2555,44 +2555,33 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) case 3: op = rv_op_csrrc; break; case 4: if (dec->cfg && dec->cfg->ext_zimop) { - int imm_mop5, imm_mop3, reg_num; - if ((extract32(inst, 22, 10) & 0b1011001111) - == 0b1000000111) { - imm_mop5 = deposit32(deposit32(extract32(inst, 20, 2), - 2, 2, - extract32(inst, 26, 2)), - 4, 1, extract32(inst, 30, 1)); - op = rv_op_mop_r; - /* if zicfiss enabled and mop5 is shadow stack */ - if (dec->cfg->ext_zicfiss && - ((imm_mop5 & 0b11100) == 0b11100)) { - /* rs1=0 means ssrdp */ - if ((inst & (0b011111 << 15)) == 0) { - op = rv_op_ssrdp; - } - /* rd=0 means sspopchk */ - reg_num = (inst >> 15) & 0b011111; - if (((inst & (0b011111 << 7)) == 0) && - ((reg_num == 1) || (reg_num == 5))) { - op = rv_op_sspopchk; + if (((inst >> 22) & 0b1011001111) == 0b1000000111) { + if (dec->cfg->ext_zicfiss + && operand_mop_r_imm(inst) == 28) { + switch (operand_rs1(inst)) { + case 0: + return &rvi_opcode_data[rv_op_ssrdp]; + case 1: + case 5: + if (operand_rd(inst) == 0) { + return &rvi_opcode_data[rv_op_sspopchk]; } + } } - } else if ((extract32(inst, 25, 7) & 0b1011001) - == 0b1000001) { - imm_mop3 = deposit32(extract32(inst, 26, 2), - 2, 1, extract32(inst, 30, 1)); - op = rv_op_mop_rr; - /* if zicfiss enabled and mop3 is shadow stack */ - if (dec->cfg->ext_zicfiss && - ((imm_mop3 & 0b111) == 0b111)) { - /* rs1=0 and rd=0 means sspush */ - reg_num = (inst >> 20) & 0b011111; - if (((inst & (0b011111 << 15)) == 0) && - ((inst & (0b011111 << 7)) == 0) && - ((reg_num == 1) || (reg_num == 5))) { - op = rv_op_sspush; - } + return &rvi_opcode_data[rv_op_mop_r]; + } + if (((inst >> 25) & 0b1011001) == 0b1000001) { + if (dec->cfg->ext_zicfiss + && operand_mop_rr_imm(inst) == 7 + && operand_rd(inst) == 0 + && operand_rs1(inst) == 0) { + switch (operand_rs2(inst)) { + case 1: + case 5: + return &rvi_opcode_data[rv_op_sspush]; + } } + return &rvi_opcode_data[rv_op_mop_rr]; } } break; From 4fec981f18fd13adc44d2c58ccfd502612f10dc8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:41 -0700 Subject: [PATCH 799/860] disas/riscv: Split rvi_opcode_data Generate separate objects for OP() instead of collecting in a table. Return pointers to objects directly. Reviewed-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-52-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 94 +- disas/riscv.c | 1981 +++++++++++++++++++++--------------------- 2 files changed, 1048 insertions(+), 1027 deletions(-) diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc index 802a521bca..cc939f557f 100644 --- a/disas/riscv-op.c.inc +++ b/disas/riscv-op.c.inc @@ -225,51 +225,51 @@ OP(fcvt_q_l, "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) OP(fcvt_q_lu, "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) OP(fmv_x_q, "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1) OP(fmv_q_x, "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1) -OP(c_addi4spn, "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(rv_op_addi)) -OP(c_fld, "c.fld", rv_codec_cl_ld, NULL, DECOMP(rv_op_fld)) -OP(c_lw, "c.lw", rv_codec_cl_lw, NULL, DECOMP(rv_op_lw)) -OP(c_flw, "c.flw", rv_codec_cl_lw, NULL, DECOMP(rv_op_flw)) -OP(c_fsd, "c.fsd", rv_codec_cs_sd, NULL, DECOMP(rv_op_fsd)) -OP(c_sw, "c.sw", rv_codec_cs_sw, NULL, DECOMP(rv_op_sw)) -OP(c_fsw, "c.fsw", rv_codec_cs_sw, NULL, DECOMP(rv_op_fsw)) -OP(c_addi, "c.addi", rv_codec_ci, NULL, DECOMP(rv_op_addi)) -OP(c_jal, "c.jal", rv_codec_cj_jal, NULL, DECOMP(rv_op_jal)) -OP(c_li, "c.li", rv_codec_ci_li, NULL, DECOMP(rv_op_addi)) -OP(c_addi16sp, "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(rv_op_addi)) -OP(c_lui, "c.lui", rv_codec_ci_lui, NULL, DECOMP(rv_op_lui)) -OP(c_srli, "c.srli", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srli)) -OP(c_srai, "c.srai", rv_codec_cb_sh6, NULL, DECOMP(rv_op_srai)) -OP(c_andi, "c.andi", rv_codec_cb_imm, NULL, DECOMP(rv_op_andi)) -OP(c_sub, "c.sub", rv_codec_cs, NULL, DECOMP(rv_op_sub)) -OP(c_xor, "c.xor", rv_codec_cs, NULL, DECOMP(rv_op_xor)) -OP(c_or, "c.or", rv_codec_cs, NULL, DECOMP(rv_op_or)) -OP(c_and, "c.and", rv_codec_cs, NULL, DECOMP(rv_op_and)) -OP(c_subw, "c.subw", rv_codec_cs, NULL, DECOMP(rv_op_subw)) -OP(c_addw, "c.addw", rv_codec_cs, NULL, DECOMP(rv_op_addw)) -OP(c_j, "c.j", rv_codec_cj, NULL, DECOMP(rv_op_j)) -OP(c_beqz, "c.beqz", rv_codec_cb, NULL, DECOMP(rv_op_beqz)) -OP(c_bnez, "c.bnez", rv_codec_cb, NULL, DECOMP(rv_op_bnez)) -OP(c_slli, "c.slli", rv_codec_ci_sh6, NULL, DECOMP(rv_op_slli)) -OP(c_fldsp, "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_fld)) -OP(c_lwsp, "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_lw)) -OP(c_flwsp, "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(rv_op_flw)) -OP(c_jr, "c.jr", rv_codec_cr_jr, NULL, DECOMP(rv_op_jr)) -OP(c_mv, "c.mv", rv_codec_cr_mv, NULL, DECOMP(rv_op_mv)) -OP(c_ebreak, "c.ebreak", rv_codec_ci_none, NULL, DECOMP(rv_op_ebreak)) -OP(c_jalr, "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(rv_op_jalr)) -OP(c_add, "c.add", rv_codec_cr, NULL, DECOMP(rv_op_add)) -OP(c_fsdsp, "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_fsd)) -OP(c_swsp, "c.swsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_sw)) -OP(c_fswsp, "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(rv_op_fsw)) -OP(c_ld, "c.ld", rv_codec_cl_ld, NULL, DECOMP(rv_op_ld)) -OP(c_sd, "c.sd", rv_codec_cs_sd, NULL, DECOMP(rv_op_sd)) -OP(c_addiw, "c.addiw", rv_codec_ci, NULL, DECOMP(rv_op_addiw)) -OP(c_ldsp, "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(rv_op_ld)) -OP(c_sdsp, "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(rv_op_sd)) -OP(c_lq, "c.lq", rv_codec_cl_lq, NULL, DECOMP(rv_op_lq)) -OP(c_sq, "c.sq", rv_codec_cs_sq, NULL, DECOMP(rv_op_sq)) -OP(c_lqsp, "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(rv_op_lq)) -OP(c_sqsp, "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(rv_op_sq)) +OP(c_addi4spn, "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(op_addi)) +OP(c_fld, "c.fld", rv_codec_cl_ld, NULL, DECOMP(op_fld)) +OP(c_lw, "c.lw", rv_codec_cl_lw, NULL, DECOMP(op_lw)) +OP(c_flw, "c.flw", rv_codec_cl_lw, NULL, DECOMP(op_flw)) +OP(c_fsd, "c.fsd", rv_codec_cs_sd, NULL, DECOMP(op_fsd)) +OP(c_sw, "c.sw", rv_codec_cs_sw, NULL, DECOMP(op_sw)) +OP(c_fsw, "c.fsw", rv_codec_cs_sw, NULL, DECOMP(op_fsw)) +OP(c_addi, "c.addi", rv_codec_ci, NULL, DECOMP(op_addi)) +OP(c_jal, "c.jal", rv_codec_cj_jal, NULL, DECOMP(op_jal)) +OP(c_li, "c.li", rv_codec_ci_li, NULL, DECOMP(op_addi)) +OP(c_addi16sp, "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(op_addi)) +OP(c_lui, "c.lui", rv_codec_ci_lui, NULL, DECOMP(op_lui)) +OP(c_srli, "c.srli", rv_codec_cb_sh6, NULL, DECOMP(op_srli)) +OP(c_srai, "c.srai", rv_codec_cb_sh6, NULL, DECOMP(op_srai)) +OP(c_andi, "c.andi", rv_codec_cb_imm, NULL, DECOMP(op_andi)) +OP(c_sub, "c.sub", rv_codec_cs, NULL, DECOMP(op_sub)) +OP(c_xor, "c.xor", rv_codec_cs, NULL, DECOMP(op_xor)) +OP(c_or, "c.or", rv_codec_cs, NULL, DECOMP(op_or)) +OP(c_and, "c.and", rv_codec_cs, NULL, DECOMP(op_and)) +OP(c_subw, "c.subw", rv_codec_cs, NULL, DECOMP(op_subw)) +OP(c_addw, "c.addw", rv_codec_cs, NULL, DECOMP(op_addw)) +OP(c_j, "c.j", rv_codec_cj, NULL, DECOMP(op_j)) +OP(c_beqz, "c.beqz", rv_codec_cb, NULL, DECOMP(op_beqz)) +OP(c_bnez, "c.bnez", rv_codec_cb, NULL, DECOMP(op_bnez)) +OP(c_slli, "c.slli", rv_codec_ci_sh6, NULL, DECOMP(op_slli)) +OP(c_fldsp, "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_fld)) +OP(c_lwsp, "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_lw)) +OP(c_flwsp, "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_flw)) +OP(c_jr, "c.jr", rv_codec_cr_jr, NULL, DECOMP(op_jr)) +OP(c_mv, "c.mv", rv_codec_cr_mv, NULL, DECOMP(op_mv)) +OP(c_ebreak, "c.ebreak", rv_codec_ci_none, NULL, DECOMP(op_ebreak)) +OP(c_jalr, "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(op_jalr)) +OP(c_add, "c.add", rv_codec_cr, NULL, DECOMP(op_add)) +OP(c_fsdsp, "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(op_fsd)) +OP(c_swsp, "c.swsp", rv_codec_css_swsp, NULL, DECOMP(op_sw)) +OP(c_fswsp, "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(op_fsw)) +OP(c_ld, "c.ld", rv_codec_cl_ld, NULL, DECOMP(op_ld)) +OP(c_sd, "c.sd", rv_codec_cs_sd, NULL, DECOMP(op_sd)) +OP(c_addiw, "c.addiw", rv_codec_ci, NULL, DECOMP(op_addiw)) +OP(c_ldsp, "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_ld)) +OP(c_sdsp, "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(op_sd)) +OP(c_lq, "c.lq", rv_codec_cl_lq, NULL, DECOMP(op_lq)) +OP(c_sq, "c.sq", rv_codec_cs_sq, NULL, DECOMP(op_sq)) +OP(c_lqsp, "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(op_lq)) +OP(c_sqsp, "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(op_sq)) OP(nop, "nop", rv_codec_illegal, rv_fmt_none) OP(mv, "mv", rv_codec_illegal, rv_fmt_rd_rs1, rvcp_mv) OP(not, "not", rv_codec_illegal, rv_fmt_rd_rs1) @@ -904,8 +904,8 @@ OP(sspopchk, "sspopchk", rv_codec_r, rv_fmt_rs1) OP(ssrdp, "ssrdp", rv_codec_r, rv_fmt_rd) OP(ssamoswap_w, "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(ssamoswap_d, "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(c_sspush, "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspush)) -OP(c_sspopchk, "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(rv_op_sspopchk)) +OP(c_sspush, "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(op_sspush)) +OP(c_sspopchk, "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(op_sspopchk)) OP(cbo_inval, "cbo.inval", rv_codec_r, rv_fmt_rs1) OP(cbo_clean, "cbo.clean", rv_codec_r, rv_fmt_rs1) OP(cbo_flush, "cbo.flush", rv_codec_r, rv_fmt_rs1) diff --git a/disas/riscv.c b/disas/riscv.c index 7ea65ba2e7..b8099cbdf8 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -28,12 +28,6 @@ #include "disas/riscv-xventana.h" #include "disas/riscv-xlrbr.h" -typedef enum { -#define OP(N, ...) rv_op_##N, -#include "riscv-op.c.inc" -#undef OP -} rv_op; - /* register names */ static const char rv_ireg_name_sym[32][5] = { @@ -113,140 +107,143 @@ static const rvc_constraint rvcc_true[] = { rvc_end }; /* pseudo-instruction metadata */ -static const rv_opcode_data rvi_opcode_data[]; +/* Forward declare the all the opcodes so that we may link them. */ +#define OP(N, ...) static const rv_opcode_data op_##N; +#include "riscv-op.c.inc" +#undef OP static const rv_comp_data rvcp_jal[] = { - { &rvi_opcode_data[rv_op_j], rvcc_j }, - { &rvi_opcode_data[rv_op_jal_ra], rvcc_jal_ra }, + { &op_j, rvcc_j }, + { &op_jal_ra, rvcc_jal_ra }, { }, }; static const rv_comp_data rvcp_jalr[] = { - { &rvi_opcode_data[rv_op_jr], rvcc_jr }, - { &rvi_opcode_data[rv_op_jalr_ra], rvcc_jalr_ra }, + { &op_jr, rvcc_jr }, + { &op_jalr_ra, rvcc_jalr_ra }, { }, }; static const rv_comp_data rvcp_jr[] = { - { &rvi_opcode_data[rv_op_ret], rvcc_ret }, + { &op_ret, rvcc_ret }, { }, }; static const rv_comp_data rvcp_beq[] = { - { &rvi_opcode_data[rv_op_beqz], rvcc_beqz }, + { &op_beqz, rvcc_beqz }, { }, }; static const rv_comp_data rvcp_bne[] = { - { &rvi_opcode_data[rv_op_bnez], rvcc_bnez }, + { &op_bnez, rvcc_bnez }, { }, }; static const rv_comp_data rvcp_blt[] = { - { &rvi_opcode_data[rv_op_bltz], rvcc_bltz }, - { &rvi_opcode_data[rv_op_bgtz], rvcc_bgtz }, + { &op_bltz, rvcc_bltz }, + { &op_bgtz, rvcc_bgtz }, { }, }; static const rv_comp_data rvcp_bge[] = { - { &rvi_opcode_data[rv_op_blez], rvcc_blez }, - { &rvi_opcode_data[rv_op_bgez], rvcc_bgez }, + { &op_blez, rvcc_blez }, + { &op_bgez, rvcc_bgez }, { }, }; static const rv_comp_data rvcp_addi[] = { - { &rvi_opcode_data[rv_op_mv], rvcc_mv }, + { &op_mv, rvcc_mv }, { }, }; static const rv_comp_data rvcp_mv[] = { - { &rvi_opcode_data[rv_op_nop], rvcc_nop }, + { &op_nop, rvcc_nop }, { }, }; static const rv_comp_data rvcp_sltiu[] = { - { &rvi_opcode_data[rv_op_seqz], rvcc_seqz }, + { &op_seqz, rvcc_seqz }, { }, }; static const rv_comp_data rvcp_xori[] = { - { &rvi_opcode_data[rv_op_not], rvcc_not }, + { &op_not, rvcc_not }, { }, }; static const rv_comp_data rvcp_sub[] = { - { &rvi_opcode_data[rv_op_neg], rvcc_neg }, + { &op_neg, rvcc_neg }, { }, }; static const rv_comp_data rvcp_slt[] = { - { &rvi_opcode_data[rv_op_sltz], rvcc_sltz }, - { &rvi_opcode_data[rv_op_sgtz], rvcc_sgtz }, + { &op_sltz, rvcc_sltz }, + { &op_sgtz, rvcc_sgtz }, { }, }; static const rv_comp_data rvcp_sltu[] = { - { &rvi_opcode_data[rv_op_snez], rvcc_snez }, + { &op_snez, rvcc_snez }, { }, }; static const rv_comp_data rvcp_addiw[] = { - { &rvi_opcode_data[rv_op_sext_w], rvcc_sext_w }, + { &op_sext_w, rvcc_sext_w }, { }, }; static const rv_comp_data rvcp_subw[] = { - { &rvi_opcode_data[rv_op_negw], rvcc_negw }, + { &op_negw, rvcc_negw }, { }, }; static const rv_comp_data rvcp_fsgnj_s[] = { - { &rvi_opcode_data[rv_op_fmv_s], rvcc_fmv_s }, + { &op_fmv_s, rvcc_fmv_s }, { }, }; static const rv_comp_data rvcp_fsgnjn_s[] = { - { &rvi_opcode_data[rv_op_fneg_s], rvcc_fneg_s }, + { &op_fneg_s, rvcc_fneg_s }, { }, }; static const rv_comp_data rvcp_fsgnjx_s[] = { - { &rvi_opcode_data[rv_op_fabs_s], rvcc_fabs_s }, + { &op_fabs_s, rvcc_fabs_s }, { }, }; static const rv_comp_data rvcp_fsgnj_d[] = { - { &rvi_opcode_data[rv_op_fmv_d], rvcc_fmv_d }, + { &op_fmv_d, rvcc_fmv_d }, { }, }; static const rv_comp_data rvcp_fsgnjn_d[] = { - { &rvi_opcode_data[rv_op_fneg_d], rvcc_fneg_d }, + { &op_fneg_d, rvcc_fneg_d }, { }, }; static const rv_comp_data rvcp_fsgnjx_d[] = { - { &rvi_opcode_data[rv_op_fabs_d], rvcc_fabs_d }, + { &op_fabs_d, rvcc_fabs_d }, { }, }; static const rv_comp_data rvcp_fsgnj_q[] = { - { &rvi_opcode_data[rv_op_fmv_q], rvcc_fmv_q }, + { &op_fmv_q, rvcc_fmv_q }, { }, }; static const rv_comp_data rvcp_fsgnjn_q[] = { - { &rvi_opcode_data[rv_op_fneg_q], rvcc_fneg_q }, + { &op_fneg_q, rvcc_fneg_q }, { }, }; static const rv_comp_data rvcp_fsgnjx_q[] = { - { &rvi_opcode_data[rv_op_fabs_q], rvcc_fabs_q }, + { &op_fabs_q, rvcc_fabs_q }, { }, }; /* Convert compressed insns into normal insns via pseudo expansion. */ -#define DECOMP(X) &(const rv_comp_data){ &rvi_opcode_data[X], rvcc_true } +#define DECOMP(X) &(const rv_comp_data){ &X, rvcc_true } /* operand extractors */ @@ -662,11 +659,9 @@ static uint32_t operand_mop_rr_imm(rv_inst inst) /* instruction metadata */ -static const rv_opcode_data rvi_opcode_data[] = { -#define OP(N, ...) { __VA_ARGS__ }, +#define OP(N, ...) static const rv_opcode_data op_##N = { __VA_ARGS__ }; #include "riscv-op.c.inc" #undef OP -}; /* CSR names */ @@ -953,79 +948,65 @@ static const char *csr_name(int csrno) static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) { rv_inst inst = dec->inst; - rv_opcode op = rv_op_illegal; switch ((inst >> 0) & 0b11) { case 0: switch ((inst >> 13) & 0b111) { case 0: if ((inst >> 5) & 0xff) { - op = rv_op_c_addi4spn; + return &op_c_addi4spn; } break; case 1: if (isa == rv128) { - op = rv_op_c_lq; - } else { - op = rv_op_c_fld; + return &op_c_lq; } - break; - case 2: op = rv_op_c_lw; break; + return &op_c_fld; + case 2: return &op_c_lw; case 3: if (isa == rv32) { - op = rv_op_c_flw; - } else { - op = rv_op_c_ld; + return &op_c_flw; } - break; + return &op_c_ld; case 4: switch ((inst >> 10) & 0b111) { - case 0: op = rv_op_c_lbu; break; + case 0: return &op_c_lbu; case 1: if (((inst >> 6) & 1) == 0) { - op = rv_op_c_lhu; - } else { - op = rv_op_c_lh; + return &op_c_lhu; } - break; - case 2: op = rv_op_c_sb; break; + return &op_c_lh; + case 2: return &op_c_sb; case 3: if (((inst >> 6) & 1) == 0) { - op = rv_op_c_sh; + return &op_c_sh; } break; } break; case 5: if (isa == rv128) { - op = rv_op_c_sq; - } else { - op = rv_op_c_fsd; + return &op_c_sq; } - break; - case 6: op = rv_op_c_sw; break; + return &op_c_fsd; + case 6: return &op_c_sw; case 7: if (isa == rv32) { - op = rv_op_c_fsw; - } else { - op = rv_op_c_sd; + return &op_c_fsw; } - break; + return &op_c_sd; } break; case 1: switch ((inst >> 13) & 0b111) { case 0: - op = rv_op_c_addi; /* or unspecified HINT */ - break; + return &op_c_addi; /* or unspecified HINT */ case 1: if (isa == rv32) { - op = rv_op_c_jal; - } else { - op = rv_op_c_addiw; + return &op_c_jal; } - break; - case 2: op = rv_op_c_li; break; + return &op_c_addiw; + case 2: return &op_c_li; case 3: if (dec->cfg && dec->cfg->ext_zcmop @@ -1033,16 +1014,16 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) && ((inst >> 11) & 0b11) == 0) { if (dec->cfg->ext_zicfiss) { switch (operand_cmop_imm(inst)) { - case 1: return &rvi_opcode_data[rv_op_c_sspush]; - case 5: return &rvi_opcode_data[rv_op_c_sspopchk]; + case 1: return &op_c_sspush; + case 5: return &op_c_sspopchk; } } - return &rvi_opcode_data[rv_op_c_mop]; + return &op_c_mop; } if (inst & ((1 << 12) | (0x1f << 2))) { switch ((inst >> 7) & 0b11111) { - case 2: op = rv_op_c_addi16sp; break; - default: op = rv_op_c_lui; break; + case 2: return &op_c_addi16sp; + default: return &op_c_lui; } } break; @@ -1051,261 +1032,252 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) case 0: /* For rv32, shamt[5]=1 is designated for custom extensions. */ if (isa != rv32 || (inst & 0x1000) == 0) { - op = rv_op_c_srli; /* or unspecified HINT */ + return &op_c_srli; /* or unspecified HINT */ } break; case 1: /* For rv32, shamt[5]=1 is designated for custom extensions. */ if (isa != rv32 || (inst & 0x1000) == 0) { - op = rv_op_c_srai; /* or unspecified HINT */ + return &op_c_srai; /* or unspecified HINT */ } break; - case 2: op = rv_op_c_andi; break; + case 2: return &op_c_andi; case 3: switch (((inst >> 10) & 0b100) | ((inst >> 5) & 0b011)) { - case 0: op = rv_op_c_sub; break; - case 1: op = rv_op_c_xor; break; - case 2: op = rv_op_c_or; break; - case 3: op = rv_op_c_and; break; + case 0: return &op_c_sub; + case 1: return &op_c_xor; + case 2: return &op_c_or; + case 3: return &op_c_and; case 4: if (isa != rv32) { - op = rv_op_c_subw; + return &op_c_subw; } break; case 5: if (isa != rv32) { - op = rv_op_c_addw; + return &op_c_addw; } break; - case 6: op = rv_op_c_mul; break; + case 6: return &op_c_mul; case 7: switch ((inst >> 2) & 0b111) { - case 0: op = rv_op_c_zext_b; break; - case 1: op = rv_op_c_sext_b; break; - case 2: op = rv_op_c_zext_h; break; - case 3: op = rv_op_c_sext_h; break; - case 4: op = rv_op_c_zext_w; break; - case 5: op = rv_op_c_not; break; + case 0: return &op_c_zext_b; + case 1: return &op_c_sext_b; + case 2: return &op_c_zext_h; + case 3: return &op_c_sext_h; + case 4: return &op_c_zext_w; + case 5: return &op_c_not; } break; } break; } break; - case 5: op = rv_op_c_j; break; - case 6: op = rv_op_c_beqz; break; - case 7: op = rv_op_c_bnez; break; + case 5: return &op_c_j; + case 6: return &op_c_beqz; + case 7: return &op_c_bnez; } break; case 2: switch ((inst >> 13) & 0b111) { case 0: if (isa != rv32 || (inst & 0x1000) == 0) { - op = rv_op_c_slli; /* or unspecified HINT */ + return &op_c_slli; /* or unspecified HINT */ } break; case 1: if (isa == rv128) { - op = rv_op_c_lqsp; - } else { - op = rv_op_c_fldsp; + return &op_c_lqsp; } - break; - case 2: op = rv_op_c_lwsp; break; + return &op_c_fldsp; + case 2: return &op_c_lwsp; case 3: if (isa == rv32) { - op = rv_op_c_flwsp; - } else { - op = rv_op_c_ldsp; + return &op_c_flwsp; } - break; + return &op_c_ldsp; case 4: switch ((inst >> 12) & 0b1) { case 0: switch ((inst >> 2) & 0b11111) { - case 0: op = rv_op_c_jr; break; - default: op = rv_op_c_mv; break; + case 0: return &op_c_jr; + default: return &op_c_mv; } break; case 1: switch ((inst >> 2) & 0b11111) { case 0: switch ((inst >> 7) & 0b11111) { - case 0: op = rv_op_c_ebreak; break; - default: op = rv_op_c_jalr; break; + case 0: return &op_c_ebreak; + default: return &op_c_jalr; } break; - default: op = rv_op_c_add; break; + default: return &op_c_add; } break; } break; case 5: if (isa == rv128) { - op = rv_op_c_sqsp; - } else { - op = rv_op_c_fsdsp; - if (dec->cfg && dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { + return &op_c_sqsp; + } + if (dec->cfg) { + if (dec->cfg->ext_zcmp && ((inst >> 12) & 0b01)) { switch ((inst >> 8) & 0b01111) { case 8: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_push; + return &op_cm_push; } break; case 10: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_pop; + return &op_cm_pop; } break; case 12: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_popretz; + return &op_cm_popretz; } break; case 14: if (((inst >> 4) & 0b01111) >= 4) { - op = rv_op_cm_popret; - } - break; - } - } else { - switch ((inst >> 10) & 0b011) { - case 0: - if (dec->cfg && !dec->cfg->ext_zcmt) { - break; - } - if (((inst >> 2) & 0xFF) >= 32) { - op = rv_op_cm_jalt; - } else { - op = rv_op_cm_jt; - } - break; - case 3: - if (dec->cfg && !dec->cfg->ext_zcmp) { - break; - } - switch ((inst >> 5) & 0b011) { - case 1: op = rv_op_cm_mvsa01; break; - case 3: op = rv_op_cm_mva01s; break; + return &op_cm_popret; } break; } } + switch ((inst >> 10) & 0b011) { + case 0: + if (!dec->cfg->ext_zcmt) { + break; + } + if (((inst >> 2) & 0xFF) >= 32) { + return &op_cm_jalt; + } + return &op_cm_jt; + case 3: + if (!dec->cfg->ext_zcmp) { + break; + } + switch ((inst >> 5) & 0b011) { + case 1: return &op_cm_mvsa01; + case 3: return &op_cm_mva01s; + } + break; + } } - break; - case 6: op = rv_op_c_swsp; break; + return &op_c_fsdsp; + case 6: return &op_c_swsp; case 7: if (isa == rv32) { - op = rv_op_c_fswsp; - } else { - op = rv_op_c_sdsp; + return &op_c_fswsp; } - break; + return &op_c_sdsp; } break; case 3: switch ((inst >> 2) & 0b11111) { case 0: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_lb; break; - case 1: op = rv_op_lh; break; - case 2: op = rv_op_lw; break; - case 3: op = rv_op_ld; break; - case 4: op = rv_op_lbu; break; - case 5: op = rv_op_lhu; break; - case 6: op = rv_op_lwu; break; - case 7: op = rv_op_ldu; break; + case 0: return &op_lb; + case 1: return &op_lh; + case 2: return &op_lw; + case 3: return &op_ld; + case 4: return &op_lbu; + case 5: return &op_lhu; + case 6: return &op_lwu; + case 7: return &op_ldu; } break; case 1: switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re8_v; break; - case 552: op = rv_op_vl2re8_v; break; - case 1576: op = rv_op_vl4re8_v; break; - case 3624: op = rv_op_vl8re8_v; break; + case 40: return &op_vl1re8_v; + case 552: return &op_vl2re8_v; + case 1576: return &op_vl4re8_v; + case 3624: return &op_vl8re8_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle8_v; break; - case 11: op = rv_op_vlm_v; break; - case 16: op = rv_op_vle8ff_v; break; + case 0: return &op_vle8_v; + case 11: return &op_vlm_v; + case 16: return &op_vle8ff_v; } break; - case 1: op = rv_op_vluxei8_v; break; - case 2: op = rv_op_vlse8_v; break; - case 3: op = rv_op_vloxei8_v; break; + case 1: return &op_vluxei8_v; + case 2: return &op_vlse8_v; + case 3: return &op_vloxei8_v; } break; - case 1: op = rv_op_flh; break; - case 2: op = rv_op_flw; break; - case 3: op = rv_op_fld; break; - case 4: op = rv_op_flq; break; + case 1: return &op_flh; + case 2: return &op_flw; + case 3: return &op_fld; + case 4: return &op_flq; case 5: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re16_v; break; - case 552: op = rv_op_vl2re16_v; break; - case 1576: op = rv_op_vl4re16_v; break; - case 3624: op = rv_op_vl8re16_v; break; + case 40: return &op_vl1re16_v; + case 552: return &op_vl2re16_v; + case 1576: return &op_vl4re16_v; + case 3624: return &op_vl8re16_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle16_v; break; - case 16: op = rv_op_vle16ff_v; break; + case 0: return &op_vle16_v; + case 16: return &op_vle16ff_v; } break; - case 1: op = rv_op_vluxei16_v; break; - case 2: op = rv_op_vlse16_v; break; - case 3: op = rv_op_vloxei16_v; break; + case 1: return &op_vluxei16_v; + case 2: return &op_vlse16_v; + case 3: return &op_vloxei16_v; } break; case 6: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re32_v; break; - case 552: op = rv_op_vl2re32_v; break; - case 1576: op = rv_op_vl4re32_v; break; - case 3624: op = rv_op_vl8re32_v; break; + case 40: return &op_vl1re32_v; + case 552: return &op_vl2re32_v; + case 1576: return &op_vl4re32_v; + case 3624: return &op_vl8re32_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle32_v; break; - case 16: op = rv_op_vle32ff_v; break; + case 0: return &op_vle32_v; + case 16: return &op_vle32ff_v; } break; - case 1: op = rv_op_vluxei32_v; break; - case 2: op = rv_op_vlse32_v; break; - case 3: op = rv_op_vloxei32_v; break; + case 1: return &op_vluxei32_v; + case 2: return &op_vlse32_v; + case 3: return &op_vloxei32_v; } break; case 7: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vl1re64_v; break; - case 552: op = rv_op_vl2re64_v; break; - case 1576: op = rv_op_vl4re64_v; break; - case 3624: op = rv_op_vl8re64_v; break; + case 40: return &op_vl1re64_v; + case 552: return &op_vl2re64_v; + case 1576: return &op_vl4re64_v; + case 3624: return &op_vl8re64_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vle64_v; break; - case 16: op = rv_op_vle64ff_v; break; + case 0: return &op_vle64_v; + case 16: return &op_vle64ff_v; } break; - case 1: op = rv_op_vluxei64_v; break; - case 2: op = rv_op_vlse64_v; break; - case 3: op = rv_op_vloxei64_v; break; + case 1: return &op_vluxei64_v; + case 2: return &op_vlse64_v; + case 3: return &op_vloxei64_v; } break; } break; case 3: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fence; break; - case 1: op = rv_op_fence_i; break; + case 0: return &op_fence; + case 1: return &op_fence_i; case 2: /* * 'lq' shares the "(...) 010 ..... 0001111" opcode space @@ -1320,204 +1292,203 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) * Anything that doesn't match these will default to 'lq'. */ switch ((inst >> 17) & 0b11111) { - case 0: op = rv_op_cbo_inval; break; - case 1: op = rv_op_cbo_clean; break; - case 2: op = rv_op_cbo_flush; break; - case 4: op = rv_op_cbo_zero; break; - default: op = rv_op_lq; break; + case 0: return &op_cbo_inval; + case 1: return &op_cbo_clean; + case 2: return &op_cbo_flush; + case 4: return &op_cbo_zero; + default: return &op_lq; } } break; case 4: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_addi; break; + case 0: return &op_addi; case 1: switch ((inst >> 27) & 0b11111) { - case 0b00000: op = rv_op_slli; break; + case 0b00000: return &op_slli; case 0b00001: switch ((inst >> 20) & 0b1111111) { - case 0b0001111: op = rv_op_zip; break; + case 0b0001111: return &op_zip; } break; case 0b00010: switch ((inst >> 20) & 0b1111111) { - case 0b0000000: op = rv_op_sha256sum0; break; - case 0b0000001: op = rv_op_sha256sum1; break; - case 0b0000010: op = rv_op_sha256sig0; break; - case 0b0000011: op = rv_op_sha256sig1; break; - case 0b0000100: op = rv_op_sha512sum0; break; - case 0b0000101: op = rv_op_sha512sum1; break; - case 0b0000110: op = rv_op_sha512sig0; break; - case 0b0000111: op = rv_op_sha512sig1; break; - case 0b0001000: op = rv_op_sm3p0; break; - case 0b0001001: op = rv_op_sm3p1; break; + case 0b0000000: return &op_sha256sum0; + case 0b0000001: return &op_sha256sum1; + case 0b0000010: return &op_sha256sig0; + case 0b0000011: return &op_sha256sig1; + case 0b0000100: return &op_sha512sum0; + case 0b0000101: return &op_sha512sum1; + case 0b0000110: return &op_sha512sig0; + case 0b0000111: return &op_sha512sig1; + case 0b0001000: return &op_sm3p0; + case 0b0001001: return &op_sm3p1; } break; - case 0b00101: op = rv_op_bseti; break; + case 0b00101: return &op_bseti; case 0b00110: switch ((inst >> 20) & 0b1111111) { - case 0b0000000: op = rv_op_aes64im; break; + case 0b0000000: return &op_aes64im; default: if (((inst >> 24) & 0b0111) == 0b001) { - op = rv_op_aes64ks1i; + return &op_aes64ks1i; } break; } break; - case 0b01001: op = rv_op_bclri; break; - case 0b01101: op = rv_op_binvi; break; + case 0b01001: return &op_bclri; + case 0b01101: return &op_binvi; case 0b01100: switch ((inst >> 20) & 0b1111111) { - case 0b0000000: op = rv_op_clz; break; - case 0b0000001: op = rv_op_ctz; break; - case 0b0000010: op = rv_op_cpop; break; + case 0b0000000: return &op_clz; + case 0b0000001: return &op_ctz; + case 0b0000010: return &op_cpop; /* 0b0000011 */ - case 0b0000100: op = rv_op_sext_b; break; - case 0b0000101: op = rv_op_sext_h; break; + case 0b0000100: return &op_sext_b; + case 0b0000101: return &op_sext_h; } break; } break; - case 2: op = rv_op_slti; break; - case 3: op = rv_op_sltiu; break; - case 4: op = rv_op_xori; break; + case 2: return &op_slti; + case 3: return &op_sltiu; + case 4: return &op_xori; case 5: switch ((inst >> 27) & 0b11111) { - case 0b00000: op = rv_op_srli; break; + case 0b00000: return &op_srli; case 0b00001: switch ((inst >> 20) & 0b1111111) { - case 0b0001111: op = rv_op_unzip; break; + case 0b0001111: return &op_unzip; } break; - case 0b00101: op = rv_op_orc_b; break; - case 0b01000: op = rv_op_srai; break; - case 0b01001: op = rv_op_bexti; break; - case 0b01100: op = rv_op_rori; break; + case 0b00101: return &op_orc_b; + case 0b01000: return &op_srai; + case 0b01001: return &op_bexti; + case 0b01100: return &op_rori; case 0b01101: switch ((inst >> 20) & 0b1111111) { case 0b0011000: if (isa == rv32) { - op = rv_op_rev8; + return &op_rev8; } break; case 0b0111000: if (isa == rv64) { - op = rv_op_rev8; + return &op_rev8; } break; - case 0b0000111: op = rv_op_brev8; break; + case 0b0000111: return &op_brev8; } break; } break; - case 6: op = rv_op_ori; break; - case 7: op = rv_op_andi; break; + case 6: return &op_ori; + case 7: return &op_andi; } break; case 5: - op = rv_op_auipc; if (dec->cfg && dec->cfg->ext_zicfilp && (((inst >> 7) & 0b11111) == 0b00000)) { - op = rv_op_lpad; + return &op_lpad; } - break; + return &op_auipc; case 6: /* OP-IMM-32 */ if (isa == rv32) { break; } switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_addiw; break; + case 0: return &op_addiw; case 1: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_slliw; break; - case 2: op = rv_op_slli_uw; break; + case 0: return &op_slliw; + case 2: return &op_slli_uw; case 24: switch ((inst >> 20) & 0b11111) { - case 0b00000: op = rv_op_clzw; break; - case 0b00001: op = rv_op_ctzw; break; - case 0b00010: op = rv_op_cpopw; break; + case 0b00000: return &op_clzw; + case 0b00001: return &op_ctzw; + case 0b00010: return &op_cpopw; } break; } break; case 5: switch ((inst >> 25) & 0b1111111) { - case 0: op = rv_op_srliw; break; - case 32: op = rv_op_sraiw; break; - case 48: op = rv_op_roriw; break; + case 0: return &op_srliw; + case 32: return &op_sraiw; + case 48: return &op_roriw; } break; } break; case 8: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_sb; break; - case 1: op = rv_op_sh; break; - case 2: op = rv_op_sw; break; - case 3: op = rv_op_sd; break; - case 4: op = rv_op_sq; break; + case 0: return &op_sb; + case 1: return &op_sh; + case 2: return &op_sw; + case 3: return &op_sd; + case 4: return &op_sq; } break; case 9: switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 20) & 0b111111111111) { - case 40: op = rv_op_vs1r_v; break; - case 552: op = rv_op_vs2r_v; break; - case 1576: op = rv_op_vs4r_v; break; - case 3624: op = rv_op_vs8r_v; break; + case 40: return &op_vs1r_v; + case 552: return &op_vs2r_v; + case 1576: return &op_vs4r_v; + case 3624: return &op_vs8r_v; } switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse8_v; break; - case 11: op = rv_op_vsm_v; break; + case 0: return &op_vse8_v; + case 11: return &op_vsm_v; } break; - case 1: op = rv_op_vsuxei8_v; break; - case 2: op = rv_op_vsse8_v; break; - case 3: op = rv_op_vsoxei8_v; break; + case 1: return &op_vsuxei8_v; + case 2: return &op_vsse8_v; + case 3: return &op_vsoxei8_v; } break; - case 1: op = rv_op_fsh; break; - case 2: op = rv_op_fsw; break; - case 3: op = rv_op_fsd; break; - case 4: op = rv_op_fsq; break; + case 1: return &op_fsh; + case 2: return &op_fsw; + case 3: return &op_fsd; + case 4: return &op_fsq; case 5: switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse16_v; break; + case 0: return &op_vse16_v; } break; - case 1: op = rv_op_vsuxei16_v; break; - case 2: op = rv_op_vsse16_v; break; - case 3: op = rv_op_vsoxei16_v; break; + case 1: return &op_vsuxei16_v; + case 2: return &op_vsse16_v; + case 3: return &op_vsoxei16_v; } break; case 6: switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse32_v; break; + case 0: return &op_vse32_v; } break; - case 1: op = rv_op_vsuxei32_v; break; - case 2: op = rv_op_vsse32_v; break; - case 3: op = rv_op_vsoxei32_v; break; + case 1: return &op_vsuxei32_v; + case 2: return &op_vsse32_v; + case 3: return &op_vsoxei32_v; } break; case 7: switch ((inst >> 26) & 0b111) { case 0: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_vse64_v; break; + case 0: return &op_vse64_v; } break; - case 1: op = rv_op_vsuxei64_v; break; - case 2: op = rv_op_vsse64_v; break; - case 3: op = rv_op_vsoxei64_v; break; + case 1: return &op_vsuxei64_v; + case 2: return &op_vsse64_v; + case 3: return &op_vsoxei64_v; } break; } @@ -1525,153 +1496,150 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) case 11: switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_amoadd_b; break; - case 1: op = rv_op_amoadd_h; break; - case 2: op = rv_op_amoadd_w; break; - case 3: op = rv_op_amoadd_d; break; - case 4: op = rv_op_amoadd_q; break; - case 8: op = rv_op_amoswap_b; break; - case 9: op = rv_op_amoswap_h; break; - case 10: op = rv_op_amoswap_w; break; - case 11: op = rv_op_amoswap_d; break; - case 12: op = rv_op_amoswap_q; break; + case 0: return &op_amoadd_b; + case 1: return &op_amoadd_h; + case 2: return &op_amoadd_w; + case 3: return &op_amoadd_d; + case 4: return &op_amoadd_q; + case 8: return &op_amoswap_b; + case 9: return &op_amoswap_h; + case 10: return &op_amoswap_w; + case 11: return &op_amoswap_d; + case 12: return &op_amoswap_q; case 18: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_lr_w; break; + case 0: return &op_lr_w; } break; case 19: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_lr_d; break; + case 0: return &op_lr_d; } break; case 20: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_lr_q; break; + case 0: return &op_lr_q; } break; - case 26: op = rv_op_sc_w; break; - case 27: op = rv_op_sc_d; break; - case 28: op = rv_op_sc_q; break; - case 32: op = rv_op_amoxor_b; break; - case 33: op = rv_op_amoxor_h; break; - case 34: op = rv_op_amoxor_w; break; - case 35: op = rv_op_amoxor_d; break; - case 36: op = rv_op_amoxor_q; break; - case 40: op = rv_op_amocas_b; break; - case 41: op = rv_op_amocas_h; break; - case 42: op = rv_op_amocas_w; break; - case 43: op = rv_op_amocas_d; break; - case 44: op = rv_op_amocas_q; break; - case 64: op = rv_op_amoor_b; break; - case 65: op = rv_op_amoor_h; break; - case 66: op = rv_op_amoor_w; break; - case 67: op = rv_op_amoor_d; break; - case 68: op = rv_op_amoor_q; break; - case 74: op = rv_op_ssamoswap_w; break; - case 75: op = rv_op_ssamoswap_d; break; - case 96: op = rv_op_amoand_b; break; - case 97: op = rv_op_amoand_h; break; - case 98: op = rv_op_amoand_w; break; - case 99: op = rv_op_amoand_d; break; - case 100: op = rv_op_amoand_q; break; - case 128: op = rv_op_amomin_b; break; - case 129: op = rv_op_amomin_h; break; - case 130: op = rv_op_amomin_w; break; - case 131: op = rv_op_amomin_d; break; - case 132: op = rv_op_amomin_q; break; - case 160: op = rv_op_amomax_b; break; - case 161: op = rv_op_amomax_h; break; - case 162: op = rv_op_amomax_w; break; - case 163: op = rv_op_amomax_d; break; - case 164: op = rv_op_amomax_q; break; - case 192: op = rv_op_amominu_b; break; - case 193: op = rv_op_amominu_h; break; - case 194: op = rv_op_amominu_w; break; - case 195: op = rv_op_amominu_d; break; - case 196: op = rv_op_amominu_q; break; - case 224: op = rv_op_amomaxu_b; break; - case 225: op = rv_op_amomaxu_h; break; - case 226: op = rv_op_amomaxu_w; break; - case 227: op = rv_op_amomaxu_d; break; - case 228: op = rv_op_amomaxu_q; break; + case 26: return &op_sc_w; + case 27: return &op_sc_d; + case 28: return &op_sc_q; + case 32: return &op_amoxor_b; + case 33: return &op_amoxor_h; + case 34: return &op_amoxor_w; + case 35: return &op_amoxor_d; + case 36: return &op_amoxor_q; + case 40: return &op_amocas_b; + case 41: return &op_amocas_h; + case 42: return &op_amocas_w; + case 43: return &op_amocas_d; + case 44: return &op_amocas_q; + case 64: return &op_amoor_b; + case 65: return &op_amoor_h; + case 66: return &op_amoor_w; + case 67: return &op_amoor_d; + case 68: return &op_amoor_q; + case 74: return &op_ssamoswap_w; + case 75: return &op_ssamoswap_d; + case 96: return &op_amoand_b; + case 97: return &op_amoand_h; + case 98: return &op_amoand_w; + case 99: return &op_amoand_d; + case 100: return &op_amoand_q; + case 128: return &op_amomin_b; + case 129: return &op_amomin_h; + case 130: return &op_amomin_w; + case 131: return &op_amomin_d; + case 132: return &op_amomin_q; + case 160: return &op_amomax_b; + case 161: return &op_amomax_h; + case 162: return &op_amomax_w; + case 163: return &op_amomax_d; + case 164: return &op_amomax_q; + case 192: return &op_amominu_b; + case 193: return &op_amominu_h; + case 194: return &op_amominu_w; + case 195: return &op_amominu_d; + case 196: return &op_amominu_q; + case 224: return &op_amomaxu_b; + case 225: return &op_amomaxu_h; + case 226: return &op_amomaxu_w; + case 227: return &op_amomaxu_d; + case 228: return &op_amomaxu_q; } break; case 12: switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 0: op = rv_op_add; break; - case 1: op = rv_op_sll; break; - case 2: op = rv_op_slt; break; - case 3: op = rv_op_sltu; break; - case 4: op = rv_op_xor; break; - case 5: op = rv_op_srl; break; - case 6: op = rv_op_or; break; - case 7: op = rv_op_and; break; - case 8: op = rv_op_mul; break; - case 9: op = rv_op_mulh; break; - case 10: op = rv_op_mulhsu; break; - case 11: op = rv_op_mulhu; break; - case 12: op = rv_op_div; break; - case 13: op = rv_op_divu; break; - case 14: op = rv_op_rem; break; - case 15: op = rv_op_remu; break; + case 0: return &op_add; + case 1: return &op_sll; + case 2: return &op_slt; + case 3: return &op_sltu; + case 4: return &op_xor; + case 5: return &op_srl; + case 6: return &op_or; + case 7: return &op_and; + case 8: return &op_mul; + case 9: return &op_mulh; + case 10: return &op_mulhsu; + case 11: return &op_mulhu; + case 12: return &op_div; + case 13: return &op_divu; + case 14: return &op_rem; + case 15: return &op_remu; case 36: if (isa == rv32 && !((inst >> 20) & 0b11111)) { - op = rv_op_zext_h; - } else { - op = rv_op_pack; + return &op_zext_h; } - break; - case 39: op = rv_op_packh; break; - - case 41: op = rv_op_clmul; break; - case 42: op = rv_op_clmulr; break; - case 43: op = rv_op_clmulh; break; - case 44: op = rv_op_min; break; - case 45: op = rv_op_minu; break; - case 46: op = rv_op_max; break; - case 47: op = rv_op_maxu; break; - case 075: op = rv_op_czero_eqz; break; - case 077: op = rv_op_czero_nez; break; - case 130: op = rv_op_sh1add; break; - case 132: op = rv_op_sh2add; break; - case 134: op = rv_op_sh3add; break; - case 161: op = rv_op_bset; break; - case 162: op = rv_op_xperm4; break; - case 164: op = rv_op_xperm8; break; - case 200: op = rv_op_aes64es; break; - case 216: op = rv_op_aes64esm; break; - case 232: op = rv_op_aes64ds; break; - case 248: op = rv_op_aes64dsm; break; - case 256: op = rv_op_sub; break; - case 260: op = rv_op_xnor; break; - case 261: op = rv_op_sra; break; - case 262: op = rv_op_orn; break; - case 263: op = rv_op_andn; break; - case 289: op = rv_op_bclr; break; - case 293: op = rv_op_bext; break; - case 320: op = rv_op_sha512sum0r; break; - case 328: op = rv_op_sha512sum1r; break; - case 336: op = rv_op_sha512sig0l; break; - case 344: op = rv_op_sha512sig1l; break; - case 368: op = rv_op_sha512sig0h; break; - case 376: op = rv_op_sha512sig1h; break; - case 385: op = rv_op_rol; break; - case 389: op = rv_op_ror; break; - case 417: op = rv_op_binv; break; - case 504: op = rv_op_aes64ks2; break; + return &op_pack; + case 39: return &op_packh; + case 41: return &op_clmul; + case 42: return &op_clmulr; + case 43: return &op_clmulh; + case 44: return &op_min; + case 45: return &op_minu; + case 46: return &op_max; + case 47: return &op_maxu; + case 075: return &op_czero_eqz; + case 077: return &op_czero_nez; + case 130: return &op_sh1add; + case 132: return &op_sh2add; + case 134: return &op_sh3add; + case 161: return &op_bset; + case 162: return &op_xperm4; + case 164: return &op_xperm8; + case 200: return &op_aes64es; + case 216: return &op_aes64esm; + case 232: return &op_aes64ds; + case 248: return &op_aes64dsm; + case 256: return &op_sub; + case 260: return &op_xnor; + case 261: return &op_sra; + case 262: return &op_orn; + case 263: return &op_andn; + case 289: return &op_bclr; + case 293: return &op_bext; + case 320: return &op_sha512sum0r; + case 328: return &op_sha512sum1r; + case 336: return &op_sha512sig0l; + case 344: return &op_sha512sig1l; + case 368: return &op_sha512sig0h; + case 376: return &op_sha512sig1h; + case 385: return &op_rol; + case 389: return &op_ror; + case 417: return &op_binv; + case 504: return &op_aes64ks2; } switch ((inst >> 25) & 0b0011111) { - case 17: op = rv_op_aes32esi; break; - case 19: op = rv_op_aes32esmi; break; - case 21: op = rv_op_aes32dsi; break; - case 23: op = rv_op_aes32dsmi; break; - case 24: op = rv_op_sm4ed; break; - case 26: op = rv_op_sm4ks; break; + case 17: return &op_aes32esi; + case 19: return &op_aes32esmi; + case 21: return &op_aes32dsi; + case 23: return &op_aes32dsmi; + case 24: return &op_sm4ed; + case 26: return &op_sm4ks; } break; - case 13: op = rv_op_lui; break; + case 13: return &op_lui; case 14: /* OP-32 */ if (isa == rv32) { @@ -1679,317 +1647,317 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) } switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 0: op = rv_op_addw; break; - case 1: op = rv_op_sllw; break; - case 5: op = rv_op_srlw; break; - case 8: op = rv_op_mulw; break; - case 12: op = rv_op_divw; break; - case 13: op = rv_op_divuw; break; - case 14: op = rv_op_remw; break; - case 15: op = rv_op_remuw; break; - case 32: op = rv_op_add_uw; break; + case 0: return &op_addw; + case 1: return &op_sllw; + case 5: return &op_srlw; + case 8: return &op_mulw; + case 12: return &op_divw; + case 13: return &op_divuw; + case 14: return &op_remw; + case 15: return &op_remuw; + case 32: return &op_add_uw; case 36: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_zext_h; break; - default: op = rv_op_packw; break; + case 0: return &op_zext_h; + default: return &op_packw; } break; - case 130: op = rv_op_sh1add_uw; break; - case 132: op = rv_op_sh2add_uw; break; - case 134: op = rv_op_sh3add_uw; break; - case 256: op = rv_op_subw; break; - case 261: op = rv_op_sraw; break; - case 385: op = rv_op_rolw; break; - case 389: op = rv_op_rorw; break; + case 130: return &op_sh1add_uw; + case 132: return &op_sh2add_uw; + case 134: return &op_sh3add_uw; + case 256: return &op_subw; + case 261: return &op_sraw; + case 385: return &op_rolw; + case 389: return &op_rorw; } break; case 16: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fmadd_s; break; - case 1: op = rv_op_fmadd_d; break; - case 3: op = rv_op_fmadd_q; break; + case 0: return &op_fmadd_s; + case 1: return &op_fmadd_d; + case 3: return &op_fmadd_q; } break; case 17: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fmsub_s; break; - case 1: op = rv_op_fmsub_d; break; - case 3: op = rv_op_fmsub_q; break; + case 0: return &op_fmsub_s; + case 1: return &op_fmsub_d; + case 3: return &op_fmsub_q; } break; case 18: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fnmsub_s; break; - case 1: op = rv_op_fnmsub_d; break; - case 3: op = rv_op_fnmsub_q; break; + case 0: return &op_fnmsub_s; + case 1: return &op_fnmsub_d; + case 3: return &op_fnmsub_q; } break; case 19: switch ((inst >> 25) & 0b11) { - case 0: op = rv_op_fnmadd_s; break; - case 1: op = rv_op_fnmadd_d; break; - case 3: op = rv_op_fnmadd_q; break; + case 0: return &op_fnmadd_s; + case 1: return &op_fnmadd_d; + case 3: return &op_fnmadd_q; } break; case 20: switch ((inst >> 25) & 0b1111111) { - case 0: op = rv_op_fadd_s; break; - case 1: op = rv_op_fadd_d; break; - case 3: op = rv_op_fadd_q; break; - case 4: op = rv_op_fsub_s; break; - case 5: op = rv_op_fsub_d; break; - case 7: op = rv_op_fsub_q; break; - case 8: op = rv_op_fmul_s; break; - case 9: op = rv_op_fmul_d; break; - case 11: op = rv_op_fmul_q; break; - case 12: op = rv_op_fdiv_s; break; - case 13: op = rv_op_fdiv_d; break; - case 15: op = rv_op_fdiv_q; break; + case 0: return &op_fadd_s; + case 1: return &op_fadd_d; + case 3: return &op_fadd_q; + case 4: return &op_fsub_s; + case 5: return &op_fsub_d; + case 7: return &op_fsub_q; + case 8: return &op_fmul_s; + case 9: return &op_fmul_d; + case 11: return &op_fmul_q; + case 12: return &op_fdiv_s; + case 13: return &op_fdiv_d; + case 15: return &op_fdiv_q; case 16: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fsgnj_s; break; - case 1: op = rv_op_fsgnjn_s; break; - case 2: op = rv_op_fsgnjx_s; break; + case 0: return &op_fsgnj_s; + case 1: return &op_fsgnjn_s; + case 2: return &op_fsgnjx_s; } break; case 17: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fsgnj_d; break; - case 1: op = rv_op_fsgnjn_d; break; - case 2: op = rv_op_fsgnjx_d; break; + case 0: return &op_fsgnj_d; + case 1: return &op_fsgnjn_d; + case 2: return &op_fsgnjx_d; } break; case 19: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fsgnj_q; break; - case 1: op = rv_op_fsgnjn_q; break; - case 2: op = rv_op_fsgnjx_q; break; + case 0: return &op_fsgnj_q; + case 1: return &op_fsgnjn_q; + case 2: return &op_fsgnjx_q; } break; case 20: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fmin_s; break; - case 1: op = rv_op_fmax_s; break; - case 2: op = rv_op_fminm_s; break; - case 3: op = rv_op_fmaxm_s; break; + case 0: return &op_fmin_s; + case 1: return &op_fmax_s; + case 2: return &op_fminm_s; + case 3: return &op_fmaxm_s; } break; case 21: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fmin_d; break; - case 1: op = rv_op_fmax_d; break; - case 2: op = rv_op_fminm_d; break; - case 3: op = rv_op_fmaxm_d; break; + case 0: return &op_fmin_d; + case 1: return &op_fmax_d; + case 2: return &op_fminm_d; + case 3: return &op_fmaxm_d; } break; case 22: switch (((inst >> 12) & 0b111)) { - case 2: op = rv_op_fminm_h; break; - case 3: op = rv_op_fmaxm_h; break; + case 2: return &op_fminm_h; + case 3: return &op_fmaxm_h; } break; case 23: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fmin_q; break; - case 1: op = rv_op_fmax_q; break; - case 2: op = rv_op_fminm_q; break; - case 3: op = rv_op_fmaxm_q; break; + case 0: return &op_fmin_q; + case 1: return &op_fmax_q; + case 2: return &op_fminm_q; + case 3: return &op_fmaxm_q; } break; case 32: switch ((inst >> 20) & 0b11111) { - case 1: op = rv_op_fcvt_s_d; break; - case 3: op = rv_op_fcvt_s_q; break; - case 4: op = rv_op_fround_s; break; - case 5: op = rv_op_froundnx_s; break; - case 6: op = rv_op_fcvt_s_bf16; break; + case 1: return &op_fcvt_s_d; + case 3: return &op_fcvt_s_q; + case 4: return &op_fround_s; + case 5: return &op_froundnx_s; + case 6: return &op_fcvt_s_bf16; } break; case 33: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_d_s; break; - case 3: op = rv_op_fcvt_d_q; break; - case 4: op = rv_op_fround_d; break; - case 5: op = rv_op_froundnx_d; break; + case 0: return &op_fcvt_d_s; + case 3: return &op_fcvt_d_q; + case 4: return &op_fround_d; + case 5: return &op_froundnx_d; } break; case 34: switch (((inst >> 20) & 0b11111)) { - case 4: op = rv_op_fround_h; break; - case 5: op = rv_op_froundnx_h; break; - case 8: op = rv_op_fcvt_bf16_s; break; + case 4: return &op_fround_h; + case 5: return &op_froundnx_h; + case 8: return &op_fcvt_bf16_s; } break; case 35: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_q_s; break; - case 1: op = rv_op_fcvt_q_d; break; - case 4: op = rv_op_fround_q; break; - case 5: op = rv_op_froundnx_q; break; + case 0: return &op_fcvt_q_s; + case 1: return &op_fcvt_q_d; + case 4: return &op_fround_q; + case 5: return &op_froundnx_q; } break; case 44: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fsqrt_s; break; + case 0: return &op_fsqrt_s; } break; case 45: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fsqrt_d; break; + case 0: return &op_fsqrt_d; } break; case 47: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fsqrt_q; break; + case 0: return &op_fsqrt_q; } break; case 80: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fle_s; break; - case 1: op = rv_op_flt_s; break; - case 2: op = rv_op_feq_s; break; - case 4: op = rv_op_fleq_s; break; - case 5: op = rv_op_fltq_s; break; + case 0: return &op_fle_s; + case 1: return &op_flt_s; + case 2: return &op_feq_s; + case 4: return &op_fleq_s; + case 5: return &op_fltq_s; } break; case 81: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fle_d; break; - case 1: op = rv_op_flt_d; break; - case 2: op = rv_op_feq_d; break; - case 4: op = rv_op_fleq_d; break; - case 5: op = rv_op_fltq_d; break; + case 0: return &op_fle_d; + case 1: return &op_flt_d; + case 2: return &op_feq_d; + case 4: return &op_fleq_d; + case 5: return &op_fltq_d; } break; case 82: switch (((inst >> 12) & 0b111)) { - case 4: op = rv_op_fleq_h; break; - case 5: op = rv_op_fltq_h; break; + case 4: return &op_fleq_h; + case 5: return &op_fltq_h; } break; case 83: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_fle_q; break; - case 1: op = rv_op_flt_q; break; - case 2: op = rv_op_feq_q; break; - case 4: op = rv_op_fleq_q; break; - case 5: op = rv_op_fltq_q; break; + case 0: return &op_fle_q; + case 1: return &op_flt_q; + case 2: return &op_feq_q; + case 4: return &op_fleq_q; + case 5: return &op_fltq_q; } break; case 89: switch (((inst >> 12) & 0b111)) { - case 0: op = rv_op_fmvp_d_x; break; + case 0: return &op_fmvp_d_x; } break; case 91: switch (((inst >> 12) & 0b111)) { - case 0: op = rv_op_fmvp_q_x; break; + case 0: return &op_fmvp_q_x; } break; case 96: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_w_s; break; - case 1: op = rv_op_fcvt_wu_s; break; - case 2: op = rv_op_fcvt_l_s; break; - case 3: op = rv_op_fcvt_lu_s; break; + case 0: return &op_fcvt_w_s; + case 1: return &op_fcvt_wu_s; + case 2: return &op_fcvt_l_s; + case 3: return &op_fcvt_lu_s; } break; case 97: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_w_d; break; - case 1: op = rv_op_fcvt_wu_d; break; - case 2: op = rv_op_fcvt_l_d; break; - case 3: op = rv_op_fcvt_lu_d; break; - case 8: op = rv_op_fcvtmod_w_d; break; + case 0: return &op_fcvt_w_d; + case 1: return &op_fcvt_wu_d; + case 2: return &op_fcvt_l_d; + case 3: return &op_fcvt_lu_d; + case 8: return &op_fcvtmod_w_d; } break; case 99: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_w_q; break; - case 1: op = rv_op_fcvt_wu_q; break; - case 2: op = rv_op_fcvt_l_q; break; - case 3: op = rv_op_fcvt_lu_q; break; + case 0: return &op_fcvt_w_q; + case 1: return &op_fcvt_wu_q; + case 2: return &op_fcvt_l_q; + case 3: return &op_fcvt_lu_q; } break; case 104: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_s_w; break; - case 1: op = rv_op_fcvt_s_wu; break; - case 2: op = rv_op_fcvt_s_l; break; - case 3: op = rv_op_fcvt_s_lu; break; + case 0: return &op_fcvt_s_w; + case 1: return &op_fcvt_s_wu; + case 2: return &op_fcvt_s_l; + case 3: return &op_fcvt_s_lu; } break; case 105: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_d_w; break; - case 1: op = rv_op_fcvt_d_wu; break; - case 2: op = rv_op_fcvt_d_l; break; - case 3: op = rv_op_fcvt_d_lu; break; + case 0: return &op_fcvt_d_w; + case 1: return &op_fcvt_d_wu; + case 2: return &op_fcvt_d_l; + case 3: return &op_fcvt_d_lu; } break; case 107: switch ((inst >> 20) & 0b11111) { - case 0: op = rv_op_fcvt_q_w; break; - case 1: op = rv_op_fcvt_q_wu; break; - case 2: op = rv_op_fcvt_q_l; break; - case 3: op = rv_op_fcvt_q_lu; break; + case 0: return &op_fcvt_q_w; + case 1: return &op_fcvt_q_wu; + case 2: return &op_fcvt_q_l; + case 3: return &op_fcvt_q_lu; } break; case 112: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_s; break; - case 1: op = rv_op_fclass_s; break; + case 0: return &op_fmv_x_s; + case 1: return &op_fclass_s; } break; case 113: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_d; break; - case 1: op = rv_op_fclass_d; break; - case 8: op = rv_op_fmvh_x_d; break; + case 0: return &op_fmv_x_d; + case 1: return &op_fclass_d; + case 8: return &op_fmvh_x_d; } break; case 114: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_h; break; + case 0: return &op_fmv_x_h; } break; case 115: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_x_q; break; - case 1: op = rv_op_fclass_q; break; - case 8: op = rv_op_fmvh_x_q; break; + case 0: return &op_fmv_x_q; + case 1: return &op_fclass_q; + case 8: return &op_fmvh_x_q; } break; case 120: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_s_x; break; - case 8: op = rv_op_fli_s; break; + case 0: return &op_fmv_s_x; + case 8: return &op_fli_s; } break; case 121: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_d_x; break; - case 8: op = rv_op_fli_d; break; + case 0: return &op_fmv_d_x; + case 8: return &op_fli_d; } break; case 122: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_h_x; break; - case 8: op = rv_op_fli_h; break; + case 0: return &op_fmv_h_x; + case 8: return &op_fli_h; } break; case 123: switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) { - case 0: op = rv_op_fmv_q_x; break; - case 8: op = rv_op_fli_q; break; + case 0: return &op_fmv_q_x; + case 8: return &op_fli_q; } break; } @@ -1998,484 +1966,537 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) switch ((inst >> 12) & 0b111) { case 0: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vadd_vv; break; - case 1: op = rv_op_vandn_vv; break; - case 2: op = rv_op_vsub_vv; break; - case 4: op = rv_op_vminu_vv; break; - case 5: op = rv_op_vmin_vv; break; - case 6: op = rv_op_vmaxu_vv; break; - case 7: op = rv_op_vmax_vv; break; - case 9: op = rv_op_vand_vv; break; - case 10: op = rv_op_vor_vv; break; - case 11: op = rv_op_vxor_vv; break; - case 12: op = rv_op_vrgather_vv; break; - case 14: op = rv_op_vrgatherei16_vv; break; + case 0: return &op_vadd_vv; + case 1: return &op_vandn_vv; + case 2: return &op_vsub_vv; + case 4: return &op_vminu_vv; + case 5: return &op_vmin_vv; + case 6: return &op_vmaxu_vv; + case 7: return &op_vmax_vv; + case 9: return &op_vand_vv; + case 10: return &op_vor_vv; + case 11: return &op_vxor_vv; + case 12: return &op_vrgather_vv; + case 14: return &op_vrgatherei16_vv; case 16: if (((inst >> 25) & 1) == 0) { - op = rv_op_vadc_vvm; + return &op_vadc_vvm; } break; - case 17: op = rv_op_vmadc_vvm; break; + case 17: return &op_vmadc_vvm; case 18: if (((inst >> 25) & 1) == 0) { - op = rv_op_vsbc_vvm; + return &op_vsbc_vvm; } break; - case 19: op = rv_op_vmsbc_vvm; break; - case 20: op = rv_op_vror_vv; break; - case 21: op = rv_op_vrol_vv; break; + case 19: return &op_vmsbc_vvm; + case 20: return &op_vror_vv; + case 21: return &op_vrol_vv; case 23: - if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vmv_v_v; - else if (((inst >> 25) & 1) == 0) - op = rv_op_vmerge_vvm; + if (((inst >> 20) & 0b111111) == 32) { + return &op_vmv_v_v; + } else if (((inst >> 25) & 1) == 0) { + return &op_vmerge_vvm; + } break; - case 24: op = rv_op_vmseq_vv; break; - case 25: op = rv_op_vmsne_vv; break; - case 26: op = rv_op_vmsltu_vv; break; - case 27: op = rv_op_vmslt_vv; break; - case 28: op = rv_op_vmsleu_vv; break; - case 29: op = rv_op_vmsle_vv; break; - case 32: op = rv_op_vsaddu_vv; break; - case 33: op = rv_op_vsadd_vv; break; - case 34: op = rv_op_vssubu_vv; break; - case 35: op = rv_op_vssub_vv; break; - case 37: op = rv_op_vsll_vv; break; - case 39: op = rv_op_vsmul_vv; break; - case 40: op = rv_op_vsrl_vv; break; - case 41: op = rv_op_vsra_vv; break; - case 42: op = rv_op_vssrl_vv; break; - case 43: op = rv_op_vssra_vv; break; - case 44: op = rv_op_vnsrl_wv; break; - case 45: op = rv_op_vnsra_wv; break; - case 46: op = rv_op_vnclipu_wv; break; - case 47: op = rv_op_vnclip_wv; break; - case 48: op = rv_op_vwredsumu_vs; break; - case 49: op = rv_op_vwredsum_vs; break; - case 53: op = rv_op_vwsll_vv; break; + case 24: return &op_vmseq_vv; + case 25: return &op_vmsne_vv; + case 26: return &op_vmsltu_vv; + case 27: return &op_vmslt_vv; + case 28: return &op_vmsleu_vv; + case 29: return &op_vmsle_vv; + case 32: return &op_vsaddu_vv; + case 33: return &op_vsadd_vv; + case 34: return &op_vssubu_vv; + case 35: return &op_vssub_vv; + case 37: return &op_vsll_vv; + case 39: return &op_vsmul_vv; + case 40: return &op_vsrl_vv; + case 41: return &op_vsra_vv; + case 42: return &op_vssrl_vv; + case 43: return &op_vssra_vv; + case 44: return &op_vnsrl_wv; + case 45: return &op_vnsra_wv; + case 46: return &op_vnclipu_wv; + case 47: return &op_vnclip_wv; + case 48: return &op_vwredsumu_vs; + case 49: return &op_vwredsum_vs; + case 53: return &op_vwsll_vv; } break; case 1: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vfadd_vv; break; - case 1: op = rv_op_vfredusum_vs; break; - case 2: op = rv_op_vfsub_vv; break; - case 3: op = rv_op_vfredosum_vs; break; - case 4: op = rv_op_vfmin_vv; break; - case 5: op = rv_op_vfredmin_vs; break; - case 6: op = rv_op_vfmax_vv; break; - case 7: op = rv_op_vfredmax_vs; break; - case 8: op = rv_op_vfsgnj_vv; break; - case 9: op = rv_op_vfsgnjn_vv; break; - case 10: op = rv_op_vfsgnjx_vv; break; + case 0: return &op_vfadd_vv; + case 1: return &op_vfredusum_vs; + case 2: return &op_vfsub_vv; + case 3: return &op_vfredosum_vs; + case 4: return &op_vfmin_vv; + case 5: return &op_vfredmin_vs; + case 6: return &op_vfmax_vv; + case 7: return &op_vfredmax_vs; + case 8: return &op_vfsgnj_vv; + case 9: return &op_vfsgnjn_vv; + case 10: return &op_vfsgnjx_vv; case 16: switch ((inst >> 15) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_f_s; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vfmv_f_s; + } } break; case 18: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vfcvt_xu_f_v; break; - case 1: op = rv_op_vfcvt_x_f_v; break; - case 2: op = rv_op_vfcvt_f_xu_v; break; - case 3: op = rv_op_vfcvt_f_x_v; break; - case 6: op = rv_op_vfcvt_rtz_xu_f_v; break; - case 7: op = rv_op_vfcvt_rtz_x_f_v; break; - case 8: op = rv_op_vfwcvt_xu_f_v; break; - case 9: op = rv_op_vfwcvt_x_f_v; break; - case 10: op = rv_op_vfwcvt_f_xu_v; break; - case 11: op = rv_op_vfwcvt_f_x_v; break; - case 12: op = rv_op_vfwcvt_f_f_v; break; - case 13: op = rv_op_vfwcvtbf16_f_f_v; break; - case 14: op = rv_op_vfwcvt_rtz_xu_f_v; break; - case 15: op = rv_op_vfwcvt_rtz_x_f_v; break; - case 16: op = rv_op_vfncvt_xu_f_w; break; - case 17: op = rv_op_vfncvt_x_f_w; break; - case 18: op = rv_op_vfncvt_f_xu_w; break; - case 19: op = rv_op_vfncvt_f_x_w; break; - case 20: op = rv_op_vfncvt_f_f_w; break; - case 21: op = rv_op_vfncvt_rod_f_f_w; break; - case 22: op = rv_op_vfncvt_rtz_xu_f_w; break; - case 23: op = rv_op_vfncvt_rtz_x_f_w; break; - case 29: op = rv_op_vfncvtbf16_f_f_w; break; + case 0: return &op_vfcvt_xu_f_v; + case 1: return &op_vfcvt_x_f_v; + case 2: return &op_vfcvt_f_xu_v; + case 3: return &op_vfcvt_f_x_v; + case 6: return &op_vfcvt_rtz_xu_f_v; + case 7: return &op_vfcvt_rtz_x_f_v; + case 8: return &op_vfwcvt_xu_f_v; + case 9: return &op_vfwcvt_x_f_v; + case 10: return &op_vfwcvt_f_xu_v; + case 11: return &op_vfwcvt_f_x_v; + case 12: return &op_vfwcvt_f_f_v; + case 13: return &op_vfwcvtbf16_f_f_v; + case 14: return &op_vfwcvt_rtz_xu_f_v; + case 15: return &op_vfwcvt_rtz_x_f_v; + case 16: return &op_vfncvt_xu_f_w; + case 17: return &op_vfncvt_x_f_w; + case 18: return &op_vfncvt_f_xu_w; + case 19: return &op_vfncvt_f_x_w; + case 20: return &op_vfncvt_f_f_w; + case 21: return &op_vfncvt_rod_f_f_w; + case 22: return &op_vfncvt_rtz_xu_f_w; + case 23: return &op_vfncvt_rtz_x_f_w; + case 29: return &op_vfncvtbf16_f_f_w; } break; case 19: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vfsqrt_v; break; - case 4: op = rv_op_vfrsqrt7_v; break; - case 5: op = rv_op_vfrec7_v; break; - case 16: op = rv_op_vfclass_v; break; + case 0: return &op_vfsqrt_v; + case 4: return &op_vfrsqrt7_v; + case 5: return &op_vfrec7_v; + case 16: return &op_vfclass_v; } break; - case 24: op = rv_op_vmfeq_vv; break; - case 25: op = rv_op_vmfle_vv; break; - case 27: op = rv_op_vmflt_vv; break; - case 28: op = rv_op_vmfne_vv; break; - case 32: op = rv_op_vfdiv_vv; break; - case 36: op = rv_op_vfmul_vv; break; - case 40: op = rv_op_vfmadd_vv; break; - case 41: op = rv_op_vfnmadd_vv; break; - case 42: op = rv_op_vfmsub_vv; break; - case 43: op = rv_op_vfnmsub_vv; break; - case 44: op = rv_op_vfmacc_vv; break; - case 45: op = rv_op_vfnmacc_vv; break; - case 46: op = rv_op_vfmsac_vv; break; - case 47: op = rv_op_vfnmsac_vv; break; - case 48: op = rv_op_vfwadd_vv; break; - case 49: op = rv_op_vfwredusum_vs; break; - case 50: op = rv_op_vfwsub_vv; break; - case 51: op = rv_op_vfwredosum_vs; break; - case 52: op = rv_op_vfwadd_wv; break; - case 54: op = rv_op_vfwsub_wv; break; - case 56: op = rv_op_vfwmul_vv; break; - case 59: op = rv_op_vfwmaccbf16_vv; break; - case 60: op = rv_op_vfwmacc_vv; break; - case 61: op = rv_op_vfwnmacc_vv; break; - case 62: op = rv_op_vfwmsac_vv; break; - case 63: op = rv_op_vfwnmsac_vv; break; + case 24: return &op_vmfeq_vv; + case 25: return &op_vmfle_vv; + case 27: return &op_vmflt_vv; + case 28: return &op_vmfne_vv; + case 32: return &op_vfdiv_vv; + case 36: return &op_vfmul_vv; + case 40: return &op_vfmadd_vv; + case 41: return &op_vfnmadd_vv; + case 42: return &op_vfmsub_vv; + case 43: return &op_vfnmsub_vv; + case 44: return &op_vfmacc_vv; + case 45: return &op_vfnmacc_vv; + case 46: return &op_vfmsac_vv; + case 47: return &op_vfnmsac_vv; + case 48: return &op_vfwadd_vv; + case 49: return &op_vfwredusum_vs; + case 50: return &op_vfwsub_vv; + case 51: return &op_vfwredosum_vs; + case 52: return &op_vfwadd_wv; + case 54: return &op_vfwsub_wv; + case 56: return &op_vfwmul_vv; + case 59: return &op_vfwmaccbf16_vv; + case 60: return &op_vfwmacc_vv; + case 61: return &op_vfwnmacc_vv; + case 62: return &op_vfwmsac_vv; + case 63: return &op_vfwnmsac_vv; } break; case 2: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vredsum_vs; break; - case 1: op = rv_op_vredand_vs; break; - case 2: op = rv_op_vredor_vs; break; - case 3: op = rv_op_vredxor_vs; break; - case 4: op = rv_op_vredminu_vs; break; - case 5: op = rv_op_vredmin_vs; break; - case 6: op = rv_op_vredmaxu_vs; break; - case 7: op = rv_op_vredmax_vs; break; - case 8: op = rv_op_vaaddu_vv; break; - case 9: op = rv_op_vaadd_vv; break; - case 10: op = rv_op_vasubu_vv; break; - case 11: op = rv_op_vasub_vv; break; - case 12: op = rv_op_vclmul_vv; break; - case 13: op = rv_op_vclmulh_vv; break; + case 0: return &op_vredsum_vs; + case 1: return &op_vredand_vs; + case 2: return &op_vredor_vs; + case 3: return &op_vredxor_vs; + case 4: return &op_vredminu_vs; + case 5: return &op_vredmin_vs; + case 6: return &op_vredmaxu_vs; + case 7: return &op_vredmax_vs; + case 8: return &op_vaaddu_vv; + case 9: return &op_vaadd_vv; + case 10: return &op_vasubu_vv; + case 11: return &op_vasub_vv; + case 12: return &op_vclmul_vv; + case 13: return &op_vclmulh_vv; case 16: switch ((inst >> 15) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vmv_x_s; break; - case 16: op = rv_op_vcpop_m; break; - case 17: op = rv_op_vfirst_m; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vmv_x_s; + } + break; + case 16: return &op_vcpop_m; + case 17: return &op_vfirst_m; } break; case 18: switch ((inst >> 15) & 0b11111) { - case 2: op = rv_op_vzext_vf8; break; - case 3: op = rv_op_vsext_vf8; break; - case 4: op = rv_op_vzext_vf4; break; - case 5: op = rv_op_vsext_vf4; break; - case 6: op = rv_op_vzext_vf2; break; - case 7: op = rv_op_vsext_vf2; break; - case 8: op = rv_op_vbrev8_v; break; - case 9: op = rv_op_vrev8_v; break; - case 10: op = rv_op_vbrev_v; break; - case 12: op = rv_op_vclz_v; break; - case 13: op = rv_op_vctz_v; break; - case 14: op = rv_op_vcpop_v; break; + case 2: return &op_vzext_vf8; + case 3: return &op_vsext_vf8; + case 4: return &op_vzext_vf4; + case 5: return &op_vsext_vf4; + case 6: return &op_vzext_vf2; + case 7: return &op_vsext_vf2; + case 8: return &op_vbrev8_v; + case 9: return &op_vrev8_v; + case 10: return &op_vbrev_v; + case 12: return &op_vclz_v; + case 13: return &op_vctz_v; + case 14: return &op_vcpop_v; } break; case 20: switch ((inst >> 15) & 0b11111) { - case 1: op = rv_op_vmsbf_m; break; - case 2: op = rv_op_vmsof_m; break; - case 3: op = rv_op_vmsif_m; break; - case 16: op = rv_op_viota_m; break; + case 1: return &op_vmsbf_m; break; + case 2: return &op_vmsof_m; + case 3: return &op_vmsif_m; + case 16: return &op_viota_m; case 17: if (((inst >> 20) & 0b11111) == 0) { - op = rv_op_vid_v; + return &op_vid_v; } break; } break; - case 23: if ((inst >> 25) & 1) op = rv_op_vcompress_vm; break; - case 24: if ((inst >> 25) & 1) op = rv_op_vmandn_mm; break; - case 25: if ((inst >> 25) & 1) op = rv_op_vmand_mm; break; - case 26: if ((inst >> 25) & 1) op = rv_op_vmor_mm; break; - case 27: if ((inst >> 25) & 1) op = rv_op_vmxor_mm; break; - case 28: if ((inst >> 25) & 1) op = rv_op_vmorn_mm; break; - case 29: if ((inst >> 25) & 1) op = rv_op_vmnand_mm; break; - case 30: if ((inst >> 25) & 1) op = rv_op_vmnor_mm; break; - case 31: if ((inst >> 25) & 1) op = rv_op_vmxnor_mm; break; - case 32: op = rv_op_vdivu_vv; break; - case 33: op = rv_op_vdiv_vv; break; - case 34: op = rv_op_vremu_vv; break; - case 35: op = rv_op_vrem_vv; break; - case 36: op = rv_op_vmulhu_vv; break; - case 37: op = rv_op_vmul_vv; break; - case 38: op = rv_op_vmulhsu_vv; break; - case 39: op = rv_op_vmulh_vv; break; - case 41: op = rv_op_vmadd_vv; break; - case 43: op = rv_op_vnmsub_vv; break; - case 45: op = rv_op_vmacc_vv; break; - case 47: op = rv_op_vnmsac_vv; break; - case 48: op = rv_op_vwaddu_vv; break; - case 49: op = rv_op_vwadd_vv; break; - case 50: op = rv_op_vwsubu_vv; break; - case 51: op = rv_op_vwsub_vv; break; - case 52: op = rv_op_vwaddu_wv; break; - case 53: op = rv_op_vwadd_wv; break; - case 54: op = rv_op_vwsubu_wv; break; - case 55: op = rv_op_vwsub_wv; break; - case 56: op = rv_op_vwmulu_vv; break; - case 58: op = rv_op_vwmulsu_vv; break; - case 59: op = rv_op_vwmul_vv; break; - case 60: op = rv_op_vwmaccu_vv; break; - case 61: op = rv_op_vwmacc_vv; break; - case 63: op = rv_op_vwmaccsu_vv; break; + case 23: + if ((inst >> 25) & 1) { + return &op_vcompress_vm; + } + break; + case 24: + if ((inst >> 25) & 1) { + return &op_vmandn_mm; + } + break; + case 25: + if ((inst >> 25) & 1) { + return &op_vmand_mm; + } + break; + case 26: + if ((inst >> 25) & 1) { + return &op_vmor_mm; + } + break; + case 27: + if ((inst >> 25) & 1) { + return &op_vmxor_mm; + } + break; + case 28: + if ((inst >> 25) & 1) { + return &op_vmorn_mm; + } + break; + case 29: + if ((inst >> 25) & 1) { + return &op_vmnand_mm; + } + break; + case 30: + if ((inst >> 25) & 1) { + return &op_vmnor_mm; + } + break; + case 31: + if ((inst >> 25) & 1) { + return &op_vmxnor_mm; + } + break; + case 32: return &op_vdivu_vv; + case 33: return &op_vdiv_vv; + case 34: return &op_vremu_vv; + case 35: return &op_vrem_vv; + case 36: return &op_vmulhu_vv; + case 37: return &op_vmul_vv; + case 38: return &op_vmulhsu_vv; + case 39: return &op_vmulh_vv; + case 41: return &op_vmadd_vv; + case 43: return &op_vnmsub_vv; + case 45: return &op_vmacc_vv; + case 47: return &op_vnmsac_vv; + case 48: return &op_vwaddu_vv; + case 49: return &op_vwadd_vv; + case 50: return &op_vwsubu_vv; + case 51: return &op_vwsub_vv; + case 52: return &op_vwaddu_wv; + case 53: return &op_vwadd_wv; + case 54: return &op_vwsubu_wv; + case 55: return &op_vwsub_wv; + case 56: return &op_vwmulu_vv; + case 58: return &op_vwmulsu_vv; + case 59: return &op_vwmul_vv; + case 60: return &op_vwmaccu_vv; + case 61: return &op_vwmacc_vv; + case 63: return &op_vwmaccsu_vv; } break; case 3: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vadd_vi; break; - case 3: op = rv_op_vrsub_vi; break; - case 9: op = rv_op_vand_vi; break; - case 10: op = rv_op_vor_vi; break; - case 11: op = rv_op_vxor_vi; break; - case 12: op = rv_op_vrgather_vi; break; - case 14: op = rv_op_vslideup_vi; break; - case 15: op = rv_op_vslidedown_vi; break; + case 0: return &op_vadd_vi; + case 3: return &op_vrsub_vi; + case 9: return &op_vand_vi; + case 10: return &op_vor_vi; + case 11: return &op_vxor_vi; + case 12: return &op_vrgather_vi; + case 14: return &op_vslideup_vi; + case 15: return &op_vslidedown_vi; case 16: if (((inst >> 25) & 1) == 0) { - op = rv_op_vadc_vim; + return &op_vadc_vim; } break; - case 17: op = rv_op_vmadc_vim; break; - case 20: case 21: op = rv_op_vror_vi; break; + case 17: return &op_vmadc_vim; + case 20: case 21: return &op_vror_vi; case 23: - if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vmv_v_i; - else if (((inst >> 25) & 1) == 0) - op = rv_op_vmerge_vim; + if (((inst >> 20) & 0b111111) == 32) { + return &op_vmv_v_i; + } else if (((inst >> 25) & 1) == 0) { + return &op_vmerge_vim; + } break; - case 24: op = rv_op_vmseq_vi; break; - case 25: op = rv_op_vmsne_vi; break; - case 28: op = rv_op_vmsleu_vi; break; - case 29: op = rv_op_vmsle_vi; break; - case 30: op = rv_op_vmsgtu_vi; break; - case 31: op = rv_op_vmsgt_vi; break; - case 32: op = rv_op_vsaddu_vi; break; - case 33: op = rv_op_vsadd_vi; break; - case 37: op = rv_op_vsll_vi; break; + case 24: return &op_vmseq_vi; + case 25: return &op_vmsne_vi; + case 28: return &op_vmsleu_vi; + case 29: return &op_vmsle_vi; + case 30: return &op_vmsgtu_vi; + case 31: return &op_vmsgt_vi; + case 32: return &op_vsaddu_vi; + case 33: return &op_vsadd_vi; + case 37: return &op_vsll_vi; case 39: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vmv1r_v; break; - case 1: op = rv_op_vmv2r_v; break; - case 3: op = rv_op_vmv4r_v; break; - case 7: op = rv_op_vmv8r_v; break; + case 0: return &op_vmv1r_v; + case 1: return &op_vmv2r_v; + case 3: return &op_vmv4r_v; + case 7: return &op_vmv8r_v; } break; - case 40: op = rv_op_vsrl_vi; break; - case 41: op = rv_op_vsra_vi; break; - case 42: op = rv_op_vssrl_vi; break; - case 43: op = rv_op_vssra_vi; break; - case 44: op = rv_op_vnsrl_wi; break; - case 45: op = rv_op_vnsra_wi; break; - case 46: op = rv_op_vnclipu_wi; break; - case 47: op = rv_op_vnclip_wi; break; - case 53: op = rv_op_vwsll_vi; break; + case 40: return &op_vsrl_vi; + case 41: return &op_vsra_vi; + case 42: return &op_vssrl_vi; + case 43: return &op_vssra_vi; + case 44: return &op_vnsrl_wi; + case 45: return &op_vnsra_wi; + case 46: return &op_vnclipu_wi; + case 47: return &op_vnclip_wi; + case 53: return &op_vwsll_vi; } break; case 4: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vadd_vx; break; - case 1: op = rv_op_vandn_vx; break; - case 2: op = rv_op_vsub_vx; break; - case 3: op = rv_op_vrsub_vx; break; - case 4: op = rv_op_vminu_vx; break; - case 5: op = rv_op_vmin_vx; break; - case 6: op = rv_op_vmaxu_vx; break; - case 7: op = rv_op_vmax_vx; break; - case 9: op = rv_op_vand_vx; break; - case 10: op = rv_op_vor_vx; break; - case 11: op = rv_op_vxor_vx; break; - case 12: op = rv_op_vrgather_vx; break; - case 14: op = rv_op_vslideup_vx; break; - case 15: op = rv_op_vslidedown_vx; break; + case 0: return &op_vadd_vx; + case 1: return &op_vandn_vx; + case 2: return &op_vsub_vx; + case 3: return &op_vrsub_vx; + case 4: return &op_vminu_vx; + case 5: return &op_vmin_vx; + case 6: return &op_vmaxu_vx; + case 7: return &op_vmax_vx; + case 9: return &op_vand_vx; + case 10: return &op_vor_vx; + case 11: return &op_vxor_vx; + case 12: return &op_vrgather_vx; + case 14: return &op_vslideup_vx; + case 15: return &op_vslidedown_vx; case 16: if (((inst >> 25) & 1) == 0) { - op = rv_op_vadc_vxm; + return &op_vadc_vxm; } break; - case 17: op = rv_op_vmadc_vxm; break; + case 17: return &op_vmadc_vxm; case 18: if (((inst >> 25) & 1) == 0) { - op = rv_op_vsbc_vxm; + return &op_vsbc_vxm; } break; - case 19: op = rv_op_vmsbc_vxm; break; - case 20: op = rv_op_vror_vx; break; - case 21: op = rv_op_vrol_vx; break; + case 19: return &op_vmsbc_vxm; + case 20: return &op_vror_vx; + case 21: return &op_vrol_vx; case 23: - if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vmv_v_x; - else if (((inst >> 25) & 1) == 0) - op = rv_op_vmerge_vxm; + if (((inst >> 20) & 0b111111) == 32) { + return &op_vmv_v_x; + } else if (((inst >> 25) & 1) == 0) { + return &op_vmerge_vxm; + } break; - case 24: op = rv_op_vmseq_vx; break; - case 25: op = rv_op_vmsne_vx; break; - case 26: op = rv_op_vmsltu_vx; break; - case 27: op = rv_op_vmslt_vx; break; - case 28: op = rv_op_vmsleu_vx; break; - case 29: op = rv_op_vmsle_vx; break; - case 30: op = rv_op_vmsgtu_vx; break; - case 31: op = rv_op_vmsgt_vx; break; - case 32: op = rv_op_vsaddu_vx; break; - case 33: op = rv_op_vsadd_vx; break; - case 34: op = rv_op_vssubu_vx; break; - case 35: op = rv_op_vssub_vx; break; - case 37: op = rv_op_vsll_vx; break; - case 39: op = rv_op_vsmul_vx; break; - case 40: op = rv_op_vsrl_vx; break; - case 41: op = rv_op_vsra_vx; break; - case 42: op = rv_op_vssrl_vx; break; - case 43: op = rv_op_vssra_vx; break; - case 44: op = rv_op_vnsrl_wx; break; - case 45: op = rv_op_vnsra_wx; break; - case 46: op = rv_op_vnclipu_wx; break; - case 47: op = rv_op_vnclip_wx; break; - case 53: op = rv_op_vwsll_vx; break; + case 24: return &op_vmseq_vx; + case 25: return &op_vmsne_vx; + case 26: return &op_vmsltu_vx; + case 27: return &op_vmslt_vx; + case 28: return &op_vmsleu_vx; + case 29: return &op_vmsle_vx; + case 30: return &op_vmsgtu_vx; + case 31: return &op_vmsgt_vx; + case 32: return &op_vsaddu_vx; + case 33: return &op_vsadd_vx; + case 34: return &op_vssubu_vx; + case 35: return &op_vssub_vx; + case 37: return &op_vsll_vx; + case 39: return &op_vsmul_vx; + case 40: return &op_vsrl_vx; + case 41: return &op_vsra_vx; + case 42: return &op_vssrl_vx; + case 43: return &op_vssra_vx; + case 44: return &op_vnsrl_wx; + case 45: return &op_vnsra_wx; + case 46: return &op_vnclipu_wx; + case 47: return &op_vnclip_wx; + case 53: return &op_vwsll_vx; } break; case 5: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_vfadd_vf; break; - case 2: op = rv_op_vfsub_vf; break; - case 4: op = rv_op_vfmin_vf; break; - case 6: op = rv_op_vfmax_vf; break; - case 8: op = rv_op_vfsgnj_vf; break; - case 9: op = rv_op_vfsgnjn_vf; break; - case 10: op = rv_op_vfsgnjx_vf; break; - case 14: op = rv_op_vfslide1up_vf; break; - case 15: op = rv_op_vfslide1down_vf; break; + case 0: return &op_vfadd_vf; + case 2: return &op_vfsub_vf; + case 4: return &op_vfmin_vf; + case 6: return &op_vfmax_vf; + case 8: return &op_vfsgnj_vf; + case 9: return &op_vfsgnjn_vf; + case 10: return &op_vfsgnjx_vf; + case 14: return &op_vfslide1up_vf; + case 15: return &op_vfslide1down_vf; case 16: switch ((inst >> 20) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vfmv_s_f; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vfmv_s_f; + } } break; case 23: - if (((inst >> 25) & 1) == 0) - op = rv_op_vfmerge_vfm; - else if (((inst >> 20) & 0b111111) == 32) - op = rv_op_vfmv_v_f; + if (((inst >> 25) & 1) == 0) { + return &op_vfmerge_vfm; + } else if (((inst >> 20) & 0b111111) == 32) { + return &op_vfmv_v_f; + } break; - case 24: op = rv_op_vmfeq_vf; break; - case 25: op = rv_op_vmfle_vf; break; - case 27: op = rv_op_vmflt_vf; break; - case 28: op = rv_op_vmfne_vf; break; - case 29: op = rv_op_vmfgt_vf; break; - case 31: op = rv_op_vmfge_vf; break; - case 32: op = rv_op_vfdiv_vf; break; - case 33: op = rv_op_vfrdiv_vf; break; - case 36: op = rv_op_vfmul_vf; break; - case 39: op = rv_op_vfrsub_vf; break; - case 40: op = rv_op_vfmadd_vf; break; - case 41: op = rv_op_vfnmadd_vf; break; - case 42: op = rv_op_vfmsub_vf; break; - case 43: op = rv_op_vfnmsub_vf; break; - case 44: op = rv_op_vfmacc_vf; break; - case 45: op = rv_op_vfnmacc_vf; break; - case 46: op = rv_op_vfmsac_vf; break; - case 47: op = rv_op_vfnmsac_vf; break; - case 48: op = rv_op_vfwadd_vf; break; - case 50: op = rv_op_vfwsub_vf; break; - case 52: op = rv_op_vfwadd_wf; break; - case 54: op = rv_op_vfwsub_wf; break; - case 56: op = rv_op_vfwmul_vf; break; - case 59: op = rv_op_vfwmaccbf16_vf; break; - case 60: op = rv_op_vfwmacc_vf; break; - case 61: op = rv_op_vfwnmacc_vf; break; - case 62: op = rv_op_vfwmsac_vf; break; - case 63: op = rv_op_vfwnmsac_vf; break; + case 24: return &op_vmfeq_vf; + case 25: return &op_vmfle_vf; + case 27: return &op_vmflt_vf; + case 28: return &op_vmfne_vf; + case 29: return &op_vmfgt_vf; + case 31: return &op_vmfge_vf; + case 32: return &op_vfdiv_vf; + case 33: return &op_vfrdiv_vf; + case 36: return &op_vfmul_vf; + case 39: return &op_vfrsub_vf; + case 40: return &op_vfmadd_vf; + case 41: return &op_vfnmadd_vf; + case 42: return &op_vfmsub_vf; + case 43: return &op_vfnmsub_vf; + case 44: return &op_vfmacc_vf; + case 45: return &op_vfnmacc_vf; + case 46: return &op_vfmsac_vf; + case 47: return &op_vfnmsac_vf; + case 48: return &op_vfwadd_vf; + case 50: return &op_vfwsub_vf; + case 52: return &op_vfwadd_wf; + case 54: return &op_vfwsub_wf; + case 56: return &op_vfwmul_vf; + case 59: return &op_vfwmaccbf16_vf; + case 60: return &op_vfwmacc_vf; + case 61: return &op_vfwnmacc_vf; + case 62: return &op_vfwmsac_vf; + case 63: return &op_vfwnmsac_vf; } break; case 6: switch ((inst >> 26) & 0b111111) { - case 8: op = rv_op_vaaddu_vx; break; - case 9: op = rv_op_vaadd_vx; break; - case 10: op = rv_op_vasubu_vx; break; - case 11: op = rv_op_vasub_vx; break; - case 12: op = rv_op_vclmul_vx; break; - case 13: op = rv_op_vclmulh_vx; break; - case 14: op = rv_op_vslide1up_vx; break; - case 15: op = rv_op_vslide1down_vx; break; + case 8: return &op_vaaddu_vx; + case 9: return &op_vaadd_vx; + case 10: return &op_vasubu_vx; + case 11: return &op_vasub_vx; + case 12: return &op_vclmul_vx; + case 13: return &op_vclmulh_vx; + case 14: return &op_vslide1up_vx; + case 15: return &op_vslide1down_vx; case 16: switch ((inst >> 20) & 0b11111) { - case 0: if ((inst >> 25) & 1) op = rv_op_vmv_s_x; break; + case 0: + if ((inst >> 25) & 1) { + return &op_vmv_s_x; + } } break; - case 32: op = rv_op_vdivu_vx; break; - case 33: op = rv_op_vdiv_vx; break; - case 34: op = rv_op_vremu_vx; break; - case 35: op = rv_op_vrem_vx; break; - case 36: op = rv_op_vmulhu_vx; break; - case 37: op = rv_op_vmul_vx; break; - case 38: op = rv_op_vmulhsu_vx; break; - case 39: op = rv_op_vmulh_vx; break; - case 41: op = rv_op_vmadd_vx; break; - case 43: op = rv_op_vnmsub_vx; break; - case 45: op = rv_op_vmacc_vx; break; - case 47: op = rv_op_vnmsac_vx; break; - case 48: op = rv_op_vwaddu_vx; break; - case 49: op = rv_op_vwadd_vx; break; - case 50: op = rv_op_vwsubu_vx; break; - case 51: op = rv_op_vwsub_vx; break; - case 52: op = rv_op_vwaddu_wx; break; - case 53: op = rv_op_vwadd_wx; break; - case 54: op = rv_op_vwsubu_wx; break; - case 55: op = rv_op_vwsub_wx; break; - case 56: op = rv_op_vwmulu_vx; break; - case 58: op = rv_op_vwmulsu_vx; break; - case 59: op = rv_op_vwmul_vx; break; - case 60: op = rv_op_vwmaccu_vx; break; - case 61: op = rv_op_vwmacc_vx; break; - case 62: op = rv_op_vwmaccus_vx; break; - case 63: op = rv_op_vwmaccsu_vx; break; + case 32: return &op_vdivu_vx; + case 33: return &op_vdiv_vx; + case 34: return &op_vremu_vx; + case 35: return &op_vrem_vx; + case 36: return &op_vmulhu_vx; + case 37: return &op_vmul_vx; + case 38: return &op_vmulhsu_vx; + case 39: return &op_vmulh_vx; + case 41: return &op_vmadd_vx; + case 43: return &op_vnmsub_vx; + case 45: return &op_vmacc_vx; + case 47: return &op_vnmsac_vx; + case 48: return &op_vwaddu_vx; + case 49: return &op_vwadd_vx; + case 50: return &op_vwsubu_vx; + case 51: return &op_vwsub_vx; + case 52: return &op_vwaddu_wx; + case 53: return &op_vwadd_wx; + case 54: return &op_vwsubu_wx; + case 55: return &op_vwsub_wx; + case 56: return &op_vwmulu_vx; + case 58: return &op_vwmulsu_vx; + case 59: return &op_vwmul_vx; + case 60: return &op_vwmaccu_vx; + case 61: return &op_vwmacc_vx; + case 62: return &op_vwmaccus_vx; + case 63: return &op_vwmaccsu_vx; } break; case 7: if (((inst >> 31) & 1) == 0) { - op = rv_op_vsetvli; + return &op_vsetvli; } else if ((inst >> 30) & 1) { - op = rv_op_vsetivli; + return &op_vsetivli; } else if (((inst >> 25) & 0b11111) == 0) { - op = rv_op_vsetvl; + return &op_vsetvl; } break; } break; case 22: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_addid; break; + case 0: return &op_addid; case 1: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_sllid; break; + case 0: return &op_sllid; } break; case 5: switch ((inst >> 26) & 0b111111) { - case 0: op = rv_op_srlid; break; - case 16: op = rv_op_sraid; break; + case 0: return &op_srlid; + case 16: return &op_sraid; } break; } break; case 24: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_beq; break; - case 1: op = rv_op_bne; break; - case 4: op = rv_op_blt; break; - case 5: op = rv_op_bge; break; - case 6: op = rv_op_bltu; break; - case 7: op = rv_op_bgeu; break; + case 0: return &op_beq; + case 1: return &op_bne; + case 4: return &op_blt; + case 5: return &op_bge; + case 6: return &op_bltu; + case 7: return &op_bgeu; } break; case 25: switch ((inst >> 12) & 0b111) { - case 0: op = rv_op_jalr; break; + case 0: return &op_jalr; } break; - case 27: op = rv_op_jal; break; + case 27: return &op_jal; case 28: switch ((inst >> 12) & 0b111) { case 0: @@ -2483,76 +2504,76 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) ((inst >> 7) & 0b000000011111)) { case 0: switch ((inst >> 15) & 0b1111111111) { - case 0: op = rv_op_ecall; break; - case 32: op = rv_op_ebreak; break; - case 64: op = rv_op_uret; break; - case 416: op = rv_op_wrs_nto; break; - case 928: op = rv_op_wrs_sto; break; + case 0: return &op_ecall; + case 32: return &op_ebreak; + case 64: return &op_uret; + case 416: return &op_wrs_nto; + case 928: return &op_wrs_sto; } break; case 256: switch ((inst >> 20) & 0b11111) { case 2: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_sret; break; + case 0: return &op_sret; } break; - case 4: op = rv_op_sfence_vm; break; + case 4: return &op_sfence_vm; case 5: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_wfi; break; + case 0: return &op_wfi; } break; } break; - case 288: op = rv_op_sfence_vma; break; + case 288: return &op_sfence_vma; case 512: switch ((inst >> 15) & 0b1111111111) { - case 64: op = rv_op_hret; break; + case 64: return &op_hret; } break; case 768: switch ((inst >> 15) & 0b1111111111) { - case 64: op = rv_op_mret; break; + case 64: return &op_mret; } break; case 1792: switch ((inst >> 15) & 0b1111111111) { - case 64: op = rv_op_mnret; break; + case 64: return &op_mnret; } break; case 1952: switch ((inst >> 15) & 0b1111111111) { - case 576: op = rv_op_dret; break; + case 576: return &op_dret; } break; } break; case 1: switch (operand_csr12(inst)) { - case 1: op = rv_op_fsflags; break; - case 2: op = rv_op_fsrm; break; - case 3: op = rv_op_fscsr; break; - default: op = rv_op_csrrw; break; + case 1: return &op_fsflags; + case 2: return &op_fsrm; + case 3: return &op_fscsr; + default: return &op_csrrw; } break; case 2: - op = rv_op_csrrs; + return &op_csrrs; if (operand_rs1(inst) == 0) { switch (operand_csr12(inst)) { - case 0x001: op = rv_op_frflags; break; - case 0x002: op = rv_op_frrm; break; - case 0x003: op = rv_op_frcsr; break; - case 0xc00: op = rv_op_rdcycle; break; - case 0xc01: op = rv_op_rdtime; break; - case 0xc02: op = rv_op_rdinstret; break; - case 0xc80: op = rv_op_rdcycleh; break; - case 0xc81: op = rv_op_rdtimeh; break; - case 0xc82: op = rv_op_rdinstreth; break; + case 0x001: return &op_frflags; + case 0x002: return &op_frrm; + case 0x003: return &op_frcsr; + case 0xc00: return &op_rdcycle; + case 0xc01: return &op_rdtime; + case 0xc02: return &op_rdinstret; + case 0xc80: return &op_rdcycleh; + case 0xc81: return &op_rdtimeh; + case 0xc82: return &op_rdinstreth; } } break; - case 3: op = rv_op_csrrc; break; + case 3: return &op_csrrc; case 4: if (dec->cfg && dec->cfg->ext_zimop) { if (((inst >> 22) & 0b1011001111) == 0b1000000111) { @@ -2560,15 +2581,15 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) && operand_mop_r_imm(inst) == 28) { switch (operand_rs1(inst)) { case 0: - return &rvi_opcode_data[rv_op_ssrdp]; + return &op_ssrdp; case 1: case 5: if (operand_rd(inst) == 0) { - return &rvi_opcode_data[rv_op_sspopchk]; + return &op_sspopchk; } } } - return &rvi_opcode_data[rv_op_mop_r]; + return &op_mop_r; } if (((inst >> 25) & 0b1011001) == 0b1000001) { if (dec->cfg->ext_zicfiss @@ -2578,79 +2599,79 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) switch (operand_rs2(inst)) { case 1: case 5: - return &rvi_opcode_data[rv_op_sspush]; + return &op_sspush; } } - return &rvi_opcode_data[rv_op_mop_rr]; + return &op_mop_rr; } } break; case 5: switch (operand_csr12(inst)) { - case 1: op = rv_op_fsflagsi; break; - case 2: op = rv_op_fsrmi; break; - default: op = rv_op_csrrwi; break; + case 1: return &op_fsflagsi; + case 2: return &op_fsrmi; + default: return &op_csrrwi; } break; - case 6: op = rv_op_csrrsi; break; - case 7: op = rv_op_csrrci; break; + case 6: return &op_csrrsi; + case 7: return &op_csrrci; } break; case 29: if (((inst >> 25) & 1) == 1 && ((inst >> 12) & 0b111) == 2) { switch ((inst >> 26) & 0b111111) { - case 32: op = rv_op_vsm3me_vv; break; - case 33: op = rv_op_vsm4k_vi; break; - case 34: op = rv_op_vaeskf1_vi; break; + case 32: return &op_vsm3me_vv; + case 33: return &op_vsm4k_vi; + case 34: return &op_vaeskf1_vi; case 40: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vaesdm_vv; break; - case 1: op = rv_op_vaesdf_vv; break; - case 2: op = rv_op_vaesem_vv; break; - case 3: op = rv_op_vaesef_vv; break; - case 16: op = rv_op_vsm4r_vv; break; - case 17: op = rv_op_vgmul_vv; break; + case 0: return &op_vaesdm_vv; + case 1: return &op_vaesdf_vv; + case 2: return &op_vaesem_vv; + case 3: return &op_vaesef_vv; + case 16: return &op_vsm4r_vv; + case 17: return &op_vgmul_vv; } break; case 41: switch ((inst >> 15) & 0b11111) { - case 0: op = rv_op_vaesdm_vs; break; - case 1: op = rv_op_vaesdf_vs; break; - case 2: op = rv_op_vaesem_vs; break; - case 3: op = rv_op_vaesef_vs; break; - case 7: op = rv_op_vaesz_vs; break; - case 16: op = rv_op_vsm4r_vs; break; + case 0: return &op_vaesdm_vs; + case 1: return &op_vaesdf_vs; + case 2: return &op_vaesem_vs; + case 3: return &op_vaesef_vs; + case 7: return &op_vaesz_vs; + case 16: return &op_vsm4r_vs; } break; - case 42: op = rv_op_vaeskf2_vi; break; - case 43: op = rv_op_vsm3c_vi; break; - case 44: op = rv_op_vghsh_vv; break; - case 45: op = rv_op_vsha2ms_vv; break; - case 46: op = rv_op_vsha2ch_vv; break; - case 47: op = rv_op_vsha2cl_vv; break; + case 42: return &op_vaeskf2_vi; + case 43: return &op_vsm3c_vi; + case 44: return &op_vghsh_vv; + case 45: return &op_vsha2ms_vv; + case 46: return &op_vsha2ch_vv; + case 47: return &op_vsha2cl_vv; } } break; case 30: switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) { - case 0: op = rv_op_addd; break; - case 1: op = rv_op_slld; break; - case 5: op = rv_op_srld; break; - case 8: op = rv_op_muld; break; - case 12: op = rv_op_divd; break; - case 13: op = rv_op_divud; break; - case 14: op = rv_op_remd; break; - case 15: op = rv_op_remud; break; - case 256: op = rv_op_subd; break; - case 261: op = rv_op_srad; break; + case 0: return &op_addd; + case 1: return &op_slld; + case 5: return &op_srld; + case 8: return &op_muld; + case 12: return &op_divd; + case 13: return &op_divud; + case 14: return &op_remd; + case 15: return &op_remud; + case 256: return &op_subd; + case 261: return &op_srad; } break; } break; } - return op == rv_op_illegal ? NULL : &rvi_opcode_data[op]; + return NULL; } /* decode operands */ @@ -3450,7 +3471,7 @@ static GString *disasm_inst(rv_isa isa, uint64_t pc, rv_inst inst, decode_inst_operands(&dec, isa, op); op = decode_inst_lift_pseudo(&dec, op); } else { - op = &rvi_opcode_data[rv_op_illegal]; + op = &op_illegal; } return format_inst(24, &dec, op); From 7fe2a88a4049f678b626474933b516eee19bb643 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Aug 2026 15:31:42 -0700 Subject: [PATCH 800/860] disas/riscv: Sort riscv-op.c.inc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To date, opcodes had to be added to the end of the list, resulting in quite the disorder. Sort via 'LANG=C sort'. Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20260812223142.349142-53-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 1704 +++++++++++++++++++++--------------------- 1 file changed, 852 insertions(+), 852 deletions(-) diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc index cc939f557f..1d334e4857 100644 --- a/disas/riscv-op.c.inc +++ b/disas/riscv-op.c.inc @@ -1,913 +1,913 @@ -OP(illegal, "illegal", rv_codec_none, rv_fmt_none) -OP(lui, "lui", rv_codec_u, rv_fmt_rd_uimm) -OP(auipc, "auipc", rv_codec_u, rv_fmt_rd_uoffset) -OP(jal, "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal) -OP(jalr, "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr) -OP(beq, "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq) -OP(bne, "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne) -OP(blt, "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt) -OP(bge, "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge) -OP(bltu, "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset) -OP(bgeu, "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset) -OP(lb, "lb", rv_codec_i, rv_fmt_rd_offset_rs1) -OP(lh, "lh", rv_codec_i, rv_fmt_rd_offset_rs1) -OP(lw, "lw", rv_codec_i, rv_fmt_rd_offset_rs1) -OP(lbu, "lbu", rv_codec_i, rv_fmt_rd_offset_rs1) -OP(lhu, "lhu", rv_codec_i, rv_fmt_rd_offset_rs1) -OP(sb, "sb", rv_codec_s, rv_fmt_rs2_offset_rs1) -OP(sh, "sh", rv_codec_s, rv_fmt_rs2_offset_rs1) -OP(sw, "sw", rv_codec_s, rv_fmt_rs2_offset_rs1) -OP(addi, "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi) -OP(slti, "slti", rv_codec_i, rv_fmt_rd_rs1_imm) -OP(sltiu, "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu) -OP(xori, "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori) -OP(ori, "ori", rv_codec_i, rv_fmt_rd_rs1_imm) -OP(andi, "andi", rv_codec_i, rv_fmt_rd_rs1_imm) -OP(slli, "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) -OP(srli, "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) -OP(srai, "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) OP(add, "add", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sub, "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub) -OP(sll, "sll", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(slt, "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt) -OP(sltu, "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu) -OP(xor, "xor", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(srl, "srl", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sra, "sra", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(or, "or", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(add_uw, "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(addd, "addd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(addi, "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi) +OP(addid, "addid", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(addiw, "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw) +OP(addw, "addw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes32dsi, "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32dsmi, "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32esi, "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes32esmi, "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) +OP(aes64ds, "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64dsm, "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64es, "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64esm, "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(aes64im, "aes64im", rv_codec_r, rv_fmt_rd_rs1) +OP(aes64ks1i, "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum) +OP(aes64ks2, "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(amoadd_b, "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_d, "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_h, "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_q, "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoadd_w, "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_b, "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_d, "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_h, "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_q, "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoand_w, "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_b, "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_d, "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_h, "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_q, "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amocas_w, "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_b, "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_d, "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_h, "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_q, "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomax_w, "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_b, "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_d, "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_h, "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_q, "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomaxu_w, "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_b, "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_d, "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_h, "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_q, "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amomin_w, "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_b, "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_d, "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_h, "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_q, "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amominu_w, "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_b, "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_d, "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_h, "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_q, "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoor_w, "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_b, "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_d, "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_h, "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_q, "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoswap_w, "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_b, "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_d, "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_h, "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_q, "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(amoxor_w, "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) OP(and, "and", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(andi, "andi", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(andn, "andn", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(auipc, "auipc", rv_codec_u, rv_fmt_rd_uoffset) +OP(bclr, "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bclri, "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(beq, "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq) +OP(beqz, "beqz", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bext, "bext", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bexti, "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(bge, "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge) +OP(bgeu, "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset) +OP(bgez, "bgez", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bgtz, "bgtz", rv_codec_illegal, rv_fmt_rs2_offset) +OP(binv, "binv", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(binvi, "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(blez, "blez", rv_codec_illegal, rv_fmt_rs2_offset) +OP(blt, "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt) +OP(bltu, "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset) +OP(bltz, "bltz", rv_codec_illegal, rv_fmt_rs1_offset) +OP(bne, "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne) +OP(bnez, "bnez", rv_codec_illegal, rv_fmt_rs1_offset) +OP(brev8, "brev8", rv_codec_r, rv_fmt_rd_rs1) +OP(bset, "bset", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(bseti, "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(c_add, "c.add", rv_codec_cr, NULL, DECOMP(op_add)) +OP(c_addi, "c.addi", rv_codec_ci, NULL, DECOMP(op_addi)) +OP(c_addi16sp, "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(op_addi)) +OP(c_addi4spn, "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(op_addi)) +OP(c_addiw, "c.addiw", rv_codec_ci, NULL, DECOMP(op_addiw)) +OP(c_addw, "c.addw", rv_codec_cs, NULL, DECOMP(op_addw)) +OP(c_and, "c.and", rv_codec_cs, NULL, DECOMP(op_and)) +OP(c_andi, "c.andi", rv_codec_cb_imm, NULL, DECOMP(op_andi)) +OP(c_beqz, "c.beqz", rv_codec_cb, NULL, DECOMP(op_beqz)) +OP(c_bnez, "c.bnez", rv_codec_cb, NULL, DECOMP(op_bnez)) +OP(c_ebreak, "c.ebreak", rv_codec_ci_none, NULL, DECOMP(op_ebreak)) +OP(c_fld, "c.fld", rv_codec_cl_ld, NULL, DECOMP(op_fld)) +OP(c_fldsp, "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_fld)) +OP(c_flw, "c.flw", rv_codec_cl_lw, NULL, DECOMP(op_flw)) +OP(c_flwsp, "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_flw)) +OP(c_fsd, "c.fsd", rv_codec_cs_sd, NULL, DECOMP(op_fsd)) +OP(c_fsdsp, "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(op_fsd)) +OP(c_fsw, "c.fsw", rv_codec_cs_sw, NULL, DECOMP(op_fsw)) +OP(c_fswsp, "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(op_fsw)) +OP(c_j, "c.j", rv_codec_cj, NULL, DECOMP(op_j)) +OP(c_jal, "c.jal", rv_codec_cj_jal, NULL, DECOMP(op_jal)) +OP(c_jalr, "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(op_jalr)) +OP(c_jr, "c.jr", rv_codec_cr_jr, NULL, DECOMP(op_jr)) +OP(c_lbu, "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) +OP(c_ld, "c.ld", rv_codec_cl_ld, NULL, DECOMP(op_ld)) +OP(c_ldsp, "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_ld)) +OP(c_lh, "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_lhu, "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_li, "c.li", rv_codec_ci_li, NULL, DECOMP(op_addi)) +OP(c_lq, "c.lq", rv_codec_cl_lq, NULL, DECOMP(op_lq)) +OP(c_lqsp, "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(op_lq)) +OP(c_lui, "c.lui", rv_codec_ci_lui, NULL, DECOMP(op_lui)) +OP(c_lw, "c.lw", rv_codec_cl_lw, NULL, DECOMP(op_lw)) +OP(c_lwsp, "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_lw)) +OP(c_mop, "c.mop", rv_codec_cmop, rv_fmt_cmop) +OP(c_mul, "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2) +OP(c_mv, "c.mv", rv_codec_cr_mv, NULL, DECOMP(op_mv)) +OP(c_not, "c.not", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_or, "c.or", rv_codec_cs, NULL, DECOMP(op_or)) +OP(c_sb, "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) +OP(c_sd, "c.sd", rv_codec_cs_sd, NULL, DECOMP(op_sd)) +OP(c_sdsp, "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(op_sd)) +OP(c_sext_b, "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_sext_h, "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_sh, "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) +OP(c_slli, "c.slli", rv_codec_ci_sh6, NULL, DECOMP(op_slli)) +OP(c_sq, "c.sq", rv_codec_cs_sq, NULL, DECOMP(op_sq)) +OP(c_sqsp, "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(op_sq)) +OP(c_srai, "c.srai", rv_codec_cb_sh6, NULL, DECOMP(op_srai)) +OP(c_srli, "c.srli", rv_codec_cb_sh6, NULL, DECOMP(op_srli)) +OP(c_sspopchk, "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(op_sspopchk)) +OP(c_sspush, "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(op_sspush)) +OP(c_sub, "c.sub", rv_codec_cs, NULL, DECOMP(op_sub)) +OP(c_subw, "c.subw", rv_codec_cs, NULL, DECOMP(op_subw)) +OP(c_sw, "c.sw", rv_codec_cs_sw, NULL, DECOMP(op_sw)) +OP(c_swsp, "c.swsp", rv_codec_css_swsp, NULL, DECOMP(op_sw)) +OP(c_xor, "c.xor", rv_codec_cs, NULL, DECOMP(op_xor)) +OP(c_zext_b, "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_zext_h, "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd) +OP(c_zext_w, "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd) +OP(cbo_clean, "cbo.clean", rv_codec_r, rv_fmt_rs1) +OP(cbo_flush, "cbo.flush", rv_codec_r, rv_fmt_rs1) +OP(cbo_inval, "cbo.inval", rv_codec_r, rv_fmt_rs1) +OP(cbo_zero, "cbo.zero", rv_codec_r, rv_fmt_rs1) +OP(clmul, "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clmulh, "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clmulr, "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(clz, "clz", rv_codec_r, rv_fmt_rd_rs1) +OP(clzw, "clzw", rv_codec_r, rv_fmt_rd_rs1) +OP(cm_jalt, "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) +OP(cm_jt, "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) +OP(cm_mva01s, "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) +OP(cm_mvsa01, "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) +OP(cm_pop, "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_popret, "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_popretz, "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) +OP(cm_push, "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist) +OP(cpop, "cpop", rv_codec_r, rv_fmt_rd_rs1) +OP(cpopw, "cpopw", rv_codec_r, rv_fmt_rd_rs1) +OP(csrrc, "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrci, "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(csrrs, "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrsi, "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(csrrw, "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1) +OP(csrrwi, "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) +OP(ctz, "ctz", rv_codec_r, rv_fmt_rd_rs1) +OP(ctzw, "ctzw", rv_codec_r, rv_fmt_rd_rs1) +OP(czero_eqz, "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(czero_nez, "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(div, "div", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divd, "divd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divu, "divu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divud, "divud", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divuw, "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(divw, "divw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(dret, "dret", rv_codec_none, rv_fmt_none) +OP(ebreak, "ebreak", rv_codec_none, rv_fmt_none) +OP(ecall, "ecall", rv_codec_none, rv_fmt_none) +OP(fabs_d, "fabs.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fabs_q, "fabs.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fabs_s, "fabs.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fadd_d, "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fadd_q, "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fadd_s, "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fclass_d, "fclass.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fclass_q, "fclass.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fclass_s, "fclass.s", rv_codec_r, rv_fmt_rd_frs1) +OP(fcvt_bf16_s, "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_l, "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_lu, "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_q, "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_s, "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_d_w, "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_d_wu, "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_l_d, "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_l_q, "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_l_s, "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_d, "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_q, "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_lu_s, "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_q_d, "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_q_l, "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_lu, "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_s, "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_q_w, "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_q_wu, "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_bf16, "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_s_d, "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_s_l, "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_lu, "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_q, "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fcvt_s_w, "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_s_wu, "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) +OP(fcvt_w_d, "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_w_q, "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_w_s, "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_d, "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_q, "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvt_wu_s, "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fcvtmod_w_d, "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) +OP(fdiv_d, "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fdiv_q, "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fdiv_s, "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) OP(fence, "fence", rv_codec_r_f, rv_fmt_pred_succ) OP(fence_i, "fence.i", rv_codec_none, rv_fmt_none) -OP(lwu, "lwu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(feq_d, "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(feq_q, "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(feq_s, "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fld, "fld", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fle_d, "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fle_q, "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fle_s, "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_d, "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_h, "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_q, "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fleq_s, "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flh, "flh", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fli_d, "fli.d", rv_codec_fli, rv_fmt_fli) +OP(fli_h, "fli.h", rv_codec_fli, rv_fmt_fli) +OP(fli_q, "fli.q", rv_codec_fli, rv_fmt_fli) +OP(fli_s, "fli.s", rv_codec_fli, rv_fmt_fli) +OP(flq, "flq", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(flt_d, "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flt_q, "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flt_s, "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_d, "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_h, "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_q, "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(fltq_s, "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) +OP(flw, "flw", rv_codec_i, rv_fmt_frd_offset_rs1) +OP(fmadd_d, "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmadd_q, "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmadd_s, "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmax_d, "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmax_q, "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmax_s, "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_d, "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_h, "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_q, "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmaxm_s, "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmin_d, "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmin_q, "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmin_s, "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_d, "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_h, "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_q, "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fminm_s, "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) +OP(fmsub_d, "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmsub_q, "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmsub_s, "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fmul_d, "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmul_q, "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmul_s, "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fmv_d, "fmv.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_d_x, "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_h_x, "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_q, "fmv.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_q_x, "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_s, "fmv.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fmv_s_x, "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1) +OP(fmv_x_d, "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_x_h, "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_x_q, "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fmv_x_s, "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvh_x_d, "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvh_x_q, "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1) +OP(fmvp_d_x, "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2) +OP(fmvp_q_x, "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2) +OP(fneg_d, "fneg.d", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fneg_q, "fneg.q", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fneg_s, "fneg.s", rv_codec_illegal, rv_fmt_frd_frs1) +OP(fnmadd_d, "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmadd_q, "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmadd_s, "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_d, "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_q, "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(fnmsub_s, "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) +OP(frcsr, "frcsr", rv_codec_i_csr, rv_fmt_rd) +OP(frflags, "frflags", rv_codec_i_csr, rv_fmt_rd) +OP(fround_d, "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_h, "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_q, "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fround_s, "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_d, "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_h, "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_q, "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(froundnx_s, "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(frrm, "frrm", rv_codec_i_csr, rv_fmt_rd) +OP(fscsr, "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsd, "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fsflags, "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsflagsi, "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm) +OP(fsgnj_d, "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d) +OP(fsgnj_q, "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q) +OP(fsgnj_s, "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s) +OP(fsgnjn_d, "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d) +OP(fsgnjn_q, "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q) +OP(fsgnjn_s, "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s) +OP(fsgnjx_d, "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d) +OP(fsgnjx_q, "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q) +OP(fsgnjx_s, "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s) +OP(fsh, "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fsq, "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(fsqrt_d, "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsqrt_q, "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsqrt_s, "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) +OP(fsrm, "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1) +OP(fsrmi, "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm) +OP(fsub_d, "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsub_q, "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsub_s, "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) +OP(fsw, "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1) +OP(hret, "hret", rv_codec_none, rv_fmt_none) +OP(illegal, "illegal", rv_codec_none, rv_fmt_none) +OP(j, "j", rv_codec_illegal, rv_fmt_offset) +OP(jal, "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal) +OP(jal_ra, "jal", rv_codec_illegal, rv_fmt_offset) +OP(jalr, "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr) +OP(jalr_ra, "jalr", rv_codec_illegal, rv_fmt_rs1) +OP(jr, "jr", rv_codec_illegal, rv_fmt_rs1, rvcp_jr) +OP(lb, "lb", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lbu, "lbu", rv_codec_i, rv_fmt_rd_offset_rs1) OP(ld, "ld", rv_codec_i, rv_fmt_rd_offset_rs1) -OP(sd, "sd", rv_codec_s, rv_fmt_rs2_offset_rs1) -OP(addiw, "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw) -OP(slliw, "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) -OP(srliw, "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) -OP(sraiw, "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) -OP(addw, "addw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(subw, "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw) -OP(sllw, "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(srlw, "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sraw, "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(ldu, "ldu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lh, "lh", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lhu, "lhu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lpad, "lpad", rv_codec_lp, rv_fmt_imm) OP(lq, "lq", rv_codec_i, rv_fmt_rd_offset_rs1) -OP(sq, "sq", rv_codec_s, rv_fmt_rs2_offset_rs1) -OP(addid, "addid", rv_codec_i, rv_fmt_rd_rs1_imm) -OP(sllid, "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) -OP(srlid, "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) -OP(sraid, "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) -OP(addd, "addd", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(subd, "subd", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(slld, "slld", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(srld, "srld", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(srad, "srad", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(lr_d, "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(lr_q, "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(lr_w, "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) +OP(lui, "lui", rv_codec_u, rv_fmt_rd_uimm) +OP(lw, "lw", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(lwu, "lwu", rv_codec_i, rv_fmt_rd_offset_rs1) +OP(max, "max", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(maxu, "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(min, "min", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(minu, "minu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(mnret, "mnret", rv_codec_none, rv_fmt_none) +OP(mop_r, "mop.r", rv_codec_mop_r, rv_fmt_mop_r) +OP(mop_rr, "mop.rr", rv_codec_mop_rr, rv_fmt_mop_rr) +OP(mret, "mret", rv_codec_none, rv_fmt_none) OP(mul, "mul", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(muld, "muld", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(mulh, "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(mulhsu, "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(mulhu, "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(div, "div", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(divu, "divu", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(rem, "rem", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(remu, "remu", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(mulw, "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(divw, "divw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(divuw, "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(remw, "remw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(remuw, "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(muld, "muld", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(divd, "divd", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(divud, "divud", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(remd, "remd", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(remud, "remud", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(lr_w, "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) -OP(sc_w, "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoswap_w, "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoadd_w, "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoxor_w, "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoor_w, "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoand_w, "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomin_w, "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomax_w, "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amominu_w, "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomaxu_w, "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(lr_d, "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) -OP(sc_d, "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoswap_d, "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoadd_d, "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoxor_d, "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoor_d, "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoand_d, "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomin_d, "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomax_d, "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amominu_d, "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomaxu_d, "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(lr_q, "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1) -OP(sc_q, "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoswap_q, "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoadd_q, "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoxor_q, "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoor_q, "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoand_q, "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomin_q, "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomax_q, "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amominu_q, "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomaxu_q, "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(ecall, "ecall", rv_codec_none, rv_fmt_none) -OP(ebreak, "ebreak", rv_codec_none, rv_fmt_none) -OP(uret, "uret", rv_codec_none, rv_fmt_none) -OP(sret, "sret", rv_codec_none, rv_fmt_none) -OP(hret, "hret", rv_codec_none, rv_fmt_none) -OP(mret, "mret", rv_codec_none, rv_fmt_none) -OP(dret, "dret", rv_codec_none, rv_fmt_none) -OP(sfence_vm, "sfence.vm", rv_codec_r, rv_fmt_rs1) -OP(sfence_vma, "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2) -OP(wfi, "wfi", rv_codec_none, rv_fmt_none) -OP(csrrw, "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1) -OP(csrrs, "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1) -OP(csrrc, "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1) -OP(csrrwi, "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) -OP(csrrsi, "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm) -OP(csrrci, "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm) -OP(flw, "flw", rv_codec_i, rv_fmt_frd_offset_rs1) -OP(fsw, "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1) -OP(fmadd_s, "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fmsub_s, "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fnmsub_s, "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fnmadd_s, "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fadd_s, "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fsub_s, "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fmul_s, "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fdiv_s, "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fsgnj_s, "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s) -OP(fsgnjn_s, "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s) -OP(fsgnjx_s, "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s) -OP(fmin_s, "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fmax_s, "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fsqrt_s, "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fle_s, "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(flt_s, "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(feq_s, "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fcvt_w_s, "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_wu_s, "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_s_w, "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fcvt_s_wu, "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fmv_x_s, "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1) -OP(fclass_s, "fclass.s", rv_codec_r, rv_fmt_rd_frs1) -OP(fmv_s_x, "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1) -OP(fcvt_l_s, "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_lu_s, "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_s_l, "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fcvt_s_lu, "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fld, "fld", rv_codec_i, rv_fmt_frd_offset_rs1) -OP(fsd, "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1) -OP(fmadd_d, "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fmsub_d, "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fnmsub_d, "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fnmadd_d, "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fadd_d, "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fsub_d, "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fmul_d, "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fdiv_d, "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fsgnj_d, "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d) -OP(fsgnjn_d, "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d) -OP(fsgnjx_d, "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d) -OP(fmin_d, "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fmax_d, "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fcvt_s_d, "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fcvt_d_s, "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fsqrt_d, "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fle_d, "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(flt_d, "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(feq_d, "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fcvt_w_d, "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_wu_d, "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_d_w, "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fcvt_d_wu, "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fclass_d, "fclass.d", rv_codec_r, rv_fmt_rd_frs1) -OP(fcvt_l_d, "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_lu_d, "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fmv_x_d, "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1) -OP(fcvt_d_l, "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fcvt_d_lu, "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fmv_d_x, "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1) -OP(flq, "flq", rv_codec_i, rv_fmt_frd_offset_rs1) -OP(fsq, "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1) -OP(fmadd_q, "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fmsub_q, "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fnmsub_q, "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fnmadd_q, "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3) -OP(fadd_q, "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fsub_q, "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fmul_q, "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fdiv_q, "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2) -OP(fsgnj_q, "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q) -OP(fsgnjn_q, "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q) -OP(fsgnjx_q, "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q) -OP(fmin_q, "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fmax_q, "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fcvt_s_q, "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fcvt_q_s, "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fcvt_d_q, "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fcvt_q_d, "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fsqrt_q, "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fle_q, "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(flt_q, "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(feq_q, "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fcvt_w_q, "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_wu_q, "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_q_w, "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fcvt_q_wu, "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fclass_q, "fclass.q", rv_codec_r, rv_fmt_rd_frs1) -OP(fcvt_l_q, "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_lu_q, "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fcvt_q_l, "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fcvt_q_lu, "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1) -OP(fmv_x_q, "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1) -OP(fmv_q_x, "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1) -OP(c_addi4spn, "c.addi4spn", rv_codec_ciw_4spn, NULL, DECOMP(op_addi)) -OP(c_fld, "c.fld", rv_codec_cl_ld, NULL, DECOMP(op_fld)) -OP(c_lw, "c.lw", rv_codec_cl_lw, NULL, DECOMP(op_lw)) -OP(c_flw, "c.flw", rv_codec_cl_lw, NULL, DECOMP(op_flw)) -OP(c_fsd, "c.fsd", rv_codec_cs_sd, NULL, DECOMP(op_fsd)) -OP(c_sw, "c.sw", rv_codec_cs_sw, NULL, DECOMP(op_sw)) -OP(c_fsw, "c.fsw", rv_codec_cs_sw, NULL, DECOMP(op_fsw)) -OP(c_addi, "c.addi", rv_codec_ci, NULL, DECOMP(op_addi)) -OP(c_jal, "c.jal", rv_codec_cj_jal, NULL, DECOMP(op_jal)) -OP(c_li, "c.li", rv_codec_ci_li, NULL, DECOMP(op_addi)) -OP(c_addi16sp, "c.addi16sp", rv_codec_ci_16sp, NULL, DECOMP(op_addi)) -OP(c_lui, "c.lui", rv_codec_ci_lui, NULL, DECOMP(op_lui)) -OP(c_srli, "c.srli", rv_codec_cb_sh6, NULL, DECOMP(op_srli)) -OP(c_srai, "c.srai", rv_codec_cb_sh6, NULL, DECOMP(op_srai)) -OP(c_andi, "c.andi", rv_codec_cb_imm, NULL, DECOMP(op_andi)) -OP(c_sub, "c.sub", rv_codec_cs, NULL, DECOMP(op_sub)) -OP(c_xor, "c.xor", rv_codec_cs, NULL, DECOMP(op_xor)) -OP(c_or, "c.or", rv_codec_cs, NULL, DECOMP(op_or)) -OP(c_and, "c.and", rv_codec_cs, NULL, DECOMP(op_and)) -OP(c_subw, "c.subw", rv_codec_cs, NULL, DECOMP(op_subw)) -OP(c_addw, "c.addw", rv_codec_cs, NULL, DECOMP(op_addw)) -OP(c_j, "c.j", rv_codec_cj, NULL, DECOMP(op_j)) -OP(c_beqz, "c.beqz", rv_codec_cb, NULL, DECOMP(op_beqz)) -OP(c_bnez, "c.bnez", rv_codec_cb, NULL, DECOMP(op_bnez)) -OP(c_slli, "c.slli", rv_codec_ci_sh6, NULL, DECOMP(op_slli)) -OP(c_fldsp, "c.fldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_fld)) -OP(c_lwsp, "c.lwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_lw)) -OP(c_flwsp, "c.flwsp", rv_codec_ci_lwsp, NULL, DECOMP(op_flw)) -OP(c_jr, "c.jr", rv_codec_cr_jr, NULL, DECOMP(op_jr)) -OP(c_mv, "c.mv", rv_codec_cr_mv, NULL, DECOMP(op_mv)) -OP(c_ebreak, "c.ebreak", rv_codec_ci_none, NULL, DECOMP(op_ebreak)) -OP(c_jalr, "c.jalr", rv_codec_cr_jalr, NULL, DECOMP(op_jalr)) -OP(c_add, "c.add", rv_codec_cr, NULL, DECOMP(op_add)) -OP(c_fsdsp, "c.fsdsp", rv_codec_css_sdsp, NULL, DECOMP(op_fsd)) -OP(c_swsp, "c.swsp", rv_codec_css_swsp, NULL, DECOMP(op_sw)) -OP(c_fswsp, "c.fswsp", rv_codec_css_swsp, NULL, DECOMP(op_fsw)) -OP(c_ld, "c.ld", rv_codec_cl_ld, NULL, DECOMP(op_ld)) -OP(c_sd, "c.sd", rv_codec_cs_sd, NULL, DECOMP(op_sd)) -OP(c_addiw, "c.addiw", rv_codec_ci, NULL, DECOMP(op_addiw)) -OP(c_ldsp, "c.ldsp", rv_codec_ci_ldsp, NULL, DECOMP(op_ld)) -OP(c_sdsp, "c.sdsp", rv_codec_css_sdsp, NULL, DECOMP(op_sd)) -OP(c_lq, "c.lq", rv_codec_cl_lq, NULL, DECOMP(op_lq)) -OP(c_sq, "c.sq", rv_codec_cs_sq, NULL, DECOMP(op_sq)) -OP(c_lqsp, "c.lqsp", rv_codec_ci_lqsp, NULL, DECOMP(op_lq)) -OP(c_sqsp, "c.sqsp", rv_codec_css_sqsp, NULL, DECOMP(op_sq)) -OP(nop, "nop", rv_codec_illegal, rv_fmt_none) OP(mv, "mv", rv_codec_illegal, rv_fmt_rd_rs1, rvcp_mv) -OP(not, "not", rv_codec_illegal, rv_fmt_rd_rs1) OP(neg, "neg", rv_codec_illegal, rv_fmt_rd_rs2) OP(negw, "negw", rv_codec_illegal, rv_fmt_rd_rs2) -OP(sext_w, "sext.w", rv_codec_illegal, rv_fmt_rd_rs1) -OP(seqz, "seqz", rv_codec_illegal, rv_fmt_rd_rs1) -OP(snez, "snez", rv_codec_illegal, rv_fmt_rd_rs2) -OP(sltz, "sltz", rv_codec_illegal, rv_fmt_rd_rs1) -OP(sgtz, "sgtz", rv_codec_illegal, rv_fmt_rd_rs2) -OP(fmv_s, "fmv.s", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fabs_s, "fabs.s", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fneg_s, "fneg.s", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fmv_d, "fmv.d", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fabs_d, "fabs.d", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fneg_d, "fneg.d", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fmv_q, "fmv.q", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fabs_q, "fabs.q", rv_codec_illegal, rv_fmt_frd_frs1) -OP(fneg_q, "fneg.q", rv_codec_illegal, rv_fmt_frd_frs1) -OP(beqz, "beqz", rv_codec_illegal, rv_fmt_rs1_offset) -OP(bnez, "bnez", rv_codec_illegal, rv_fmt_rs1_offset) -OP(blez, "blez", rv_codec_illegal, rv_fmt_rs2_offset) -OP(bgez, "bgez", rv_codec_illegal, rv_fmt_rs1_offset) -OP(bltz, "bltz", rv_codec_illegal, rv_fmt_rs1_offset) -OP(bgtz, "bgtz", rv_codec_illegal, rv_fmt_rs2_offset) -OP(jal_ra, "jal", rv_codec_illegal, rv_fmt_offset) -OP(jalr_ra, "jalr", rv_codec_illegal, rv_fmt_rs1) -OP(j, "j", rv_codec_illegal, rv_fmt_offset) -OP(ret, "ret", rv_codec_illegal, rv_fmt_none) -OP(jr, "jr", rv_codec_illegal, rv_fmt_rs1, rvcp_jr) -OP(rdcycle, "rdcycle", rv_codec_i_csr, rv_fmt_rd) -OP(rdtime, "rdtime", rv_codec_i_csr, rv_fmt_rd) -OP(rdinstret, "rdinstret", rv_codec_i_csr, rv_fmt_rd) -OP(rdcycleh, "rdcycleh", rv_codec_i_csr, rv_fmt_rd) -OP(rdtimeh, "rdtimeh", rv_codec_i_csr, rv_fmt_rd) -OP(rdinstreth, "rdinstreth", rv_codec_i_csr, rv_fmt_rd) -OP(frcsr, "frcsr", rv_codec_i_csr, rv_fmt_rd) -OP(frrm, "frrm", rv_codec_i_csr, rv_fmt_rd) -OP(frflags, "frflags", rv_codec_i_csr, rv_fmt_rd) -OP(fscsr, "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1) -OP(fsrm, "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1) -OP(fsflags, "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1) -OP(fsrmi, "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm) -OP(fsflagsi, "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm) -OP(bseti, "bseti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) -OP(bclri, "bclri", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) -OP(binvi, "binvi", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) -OP(bexti, "bexti", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) -OP(rori, "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) -OP(clz, "clz", rv_codec_r, rv_fmt_rd_rs1) -OP(ctz, "ctz", rv_codec_r, rv_fmt_rd_rs1) -OP(cpop, "cpop", rv_codec_r, rv_fmt_rd_rs1) -OP(sext_h, "sext.h", rv_codec_r, rv_fmt_rd_rs1) -OP(sext_b, "sext.b", rv_codec_r, rv_fmt_rd_rs1) -OP(xnor, "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(orn, "orn", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(andn, "andn", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(rol, "rol", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(ror, "ror", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sh1add, "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sh2add, "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sh3add, "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sh1add_uw, "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sh2add_uw, "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sh3add_uw, "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(clmul, "clmul", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(clmulr, "clmulr", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(clmulh, "clmulh", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(min, "min", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(minu, "minu", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(max, "max", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(maxu, "maxu", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(clzw, "clzw", rv_codec_r, rv_fmt_rd_rs1) -OP(ctzw, "ctzw", rv_codec_r, rv_fmt_rd_rs1) -OP(cpopw, "cpopw", rv_codec_r, rv_fmt_rd_rs1) -OP(slli_uw, "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) -OP(add_uw, "add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(rolw, "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(rorw, "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(rev8, "rev8", rv_codec_r, rv_fmt_rd_rs1) -OP(zext_h, "zext.h", rv_codec_r, rv_fmt_rd_rs1) -OP(roriw, "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(nop, "nop", rv_codec_illegal, rv_fmt_none) +OP(not, "not", rv_codec_illegal, rv_fmt_rd_rs1) +OP(or, "or", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(orc_b, "orc.b", rv_codec_r, rv_fmt_rd_rs1) -OP(bset, "bset", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(bclr, "bclr", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(binv, "binv", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(bext, "bext", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(aes32esmi, "aes32esmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) -OP(aes32esi, "aes32esi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) -OP(aes32dsmi, "aes32dsmi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) -OP(aes32dsi, "aes32dsi", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) -OP(aes64ks1i, "aes64ks1i", rv_codec_k_rnum, rv_fmt_rd_rs1_rnum) -OP(aes64ks2, "aes64ks2", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(aes64im, "aes64im", rv_codec_r, rv_fmt_rd_rs1) -OP(aes64esm, "aes64esm", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(aes64es, "aes64es", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(aes64dsm, "aes64dsm", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(aes64ds, "aes64ds", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ori, "ori", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(orn, "orn", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(pack, "pack", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(packh, "packh", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(packw, "packw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rdcycle, "rdcycle", rv_codec_i_csr, rv_fmt_rd) +OP(rdcycleh, "rdcycleh", rv_codec_i_csr, rv_fmt_rd) +OP(rdinstret, "rdinstret", rv_codec_i_csr, rv_fmt_rd) +OP(rdinstreth, "rdinstreth", rv_codec_i_csr, rv_fmt_rd) +OP(rdtime, "rdtime", rv_codec_i_csr, rv_fmt_rd) +OP(rdtimeh, "rdtimeh", rv_codec_i_csr, rv_fmt_rd) +OP(rem, "rem", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remd, "remd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remu, "remu", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remud, "remud", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remuw, "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(remw, "remw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ret, "ret", rv_codec_illegal, rv_fmt_none) +OP(rev8, "rev8", rv_codec_r, rv_fmt_rd_rs1) +OP(rol, "rol", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rolw, "rolw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ror, "ror", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(rori, "rori", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(roriw, "roriw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(rorw, "rorw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sb, "sb", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sc_d, "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sc_q, "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sc_w, "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sd, "sd", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(seqz, "seqz", rv_codec_illegal, rv_fmt_rd_rs1) +OP(sext_b, "sext.b", rv_codec_r, rv_fmt_rd_rs1) +OP(sext_h, "sext.h", rv_codec_r, rv_fmt_rd_rs1) +OP(sext_w, "sext.w", rv_codec_illegal, rv_fmt_rd_rs1) +OP(sfence_vm, "sfence.vm", rv_codec_r, rv_fmt_rs1) +OP(sfence_vma, "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2) +OP(sgtz, "sgtz", rv_codec_illegal, rv_fmt_rd_rs2) +OP(sh, "sh", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sh1add, "sh1add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh1add_uw, "sh1add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh2add, "sh2add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh2add_uw, "sh2add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh3add, "sh3add", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sh3add_uw, "sh3add.uw", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(sha256sig0, "sha256sig0", rv_codec_r, rv_fmt_rd_rs1) OP(sha256sig1, "sha256sig1", rv_codec_r, rv_fmt_rd_rs1) OP(sha256sum0, "sha256sum0", rv_codec_r, rv_fmt_rd_rs1) OP(sha256sum1, "sha256sum1", rv_codec_r, rv_fmt_rd_rs1) OP(sha512sig0, "sha512sig0", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sha512sig1, "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sha512sum0, "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sha512sum1, "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sha512sum0r, "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sha512sum1r, "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sha512sig0l, "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(sha512sig0h, "sha512sig0h", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(sha512sig1l, "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig0l, "sha512sig0l", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1, "sha512sig1", rv_codec_r, rv_fmt_rd_rs1_rs2) OP(sha512sig1h, "sha512sig1h", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sig1l, "sha512sig1l", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum0, "sha512sum0", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum0r, "sha512sum0r", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum1, "sha512sum1", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sha512sum1r, "sha512sum1r", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sll, "sll", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slld, "slld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slli, "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(slli_uw, "slli.uw", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(sllid, "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(slliw, "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(sllw, "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(slt, "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt) +OP(slti, "slti", rv_codec_i, rv_fmt_rd_rs1_imm) +OP(sltiu, "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu) +OP(sltu, "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu) +OP(sltz, "sltz", rv_codec_illegal, rv_fmt_rd_rs1) OP(sm3p0, "sm3p0", rv_codec_r, rv_fmt_rd_rs1) OP(sm3p1, "sm3p1", rv_codec_r, rv_fmt_rd_rs1) OP(sm4ed, "sm4ed", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) OP(sm4ks, "sm4ks", rv_codec_k_bs, rv_fmt_rs1_rs2_bs) -OP(brev8, "brev8", rv_codec_r, rv_fmt_rd_rs1) -OP(pack, "pack", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(packh, "packh", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(packw, "packw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(snez, "snez", rv_codec_illegal, rv_fmt_rd_rs2) +OP(sq, "sq", rv_codec_s, rv_fmt_rs2_offset_rs1) +OP(sra, "sra", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srad, "srad", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srai, "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(sraid, "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(sraiw, "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(sraw, "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(sret, "sret", rv_codec_none, rv_fmt_none) +OP(srl, "srl", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srld, "srld", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(srli, "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm) +OP(srlid, "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm) +OP(srliw, "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm) +OP(srlw, "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(ssamoswap_d, "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(ssamoswap_w, "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) +OP(sspopchk, "sspopchk", rv_codec_r, rv_fmt_rs1) +OP(sspush, "sspush", rv_codec_r, rv_fmt_rs2) +OP(ssrdp, "ssrdp", rv_codec_r, rv_fmt_rd) +OP(sub, "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub) +OP(subd, "subd", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(subw, "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw) +OP(sw, "sw", rv_codec_s, rv_fmt_rs2_offset_rs1) OP(unzip, "unzip", rv_codec_r, rv_fmt_rd_rs1) -OP(zip, "zip", rv_codec_r, rv_fmt_rd_rs1) -OP(xperm4, "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(xperm8, "xperm8", rv_codec_r, rv_fmt_rd_rs1) -OP(vle8_v, "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vle16_v, "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vle32_v, "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vle64_v, "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vse8_v, "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vse16_v, "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vse32_v, "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vse64_v, "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vlm_v, "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vsm_v, "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vlse8_v, "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vlse16_v, "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vlse32_v, "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vlse64_v, "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vsse8_v, "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vsse16_v, "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vsse32_v, "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vsse64_v, "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) -OP(vluxei8_v, "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vluxei16_v, "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vluxei32_v, "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vluxei64_v, "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vloxei8_v, "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vloxei16_v, "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vloxei32_v, "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vloxei64_v, "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsuxei8_v, "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsuxei16_v, "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsuxei32_v, "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsuxei64_v, "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsoxei8_v, "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsoxei16_v, "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsoxei32_v, "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vsoxei64_v, "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) -OP(vle8ff_v, "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vle16ff_v, "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vle32ff_v, "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vle64ff_v, "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl1re8_v, "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl1re16_v, "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl1re32_v, "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl1re64_v, "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl2re8_v, "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl2re16_v, "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl2re32_v, "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl2re64_v, "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl4re8_v, "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl4re16_v, "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl4re32_v, "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl4re64_v, "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl8re8_v, "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl8re16_v, "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl8re32_v, "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vl8re64_v, "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vs1r_v, "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vs2r_v, "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vs4r_v, "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vs8r_v, "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) -OP(vadd_vv, "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vadd_vx, "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vadd_vi, "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vsub_vv, "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vsub_vx, "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vrsub_vx, "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vrsub_vi, "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vwaddu_vv, "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwaddu_vx, "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwadd_vv, "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwadd_vx, "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwsubu_vv, "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwsubu_vx, "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwsub_vv, "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwsub_vx, "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwaddu_wv, "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwaddu_wx, "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwadd_wv, "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwadd_wx, "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwsubu_wv, "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwsubu_wx, "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwsub_wv, "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwsub_wx, "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vadc_vvm, "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) -OP(vadc_vxm, "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) -OP(vadc_vim, "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) -OP(vmadc_vvm, "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) -OP(vmadc_vxm, "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) -OP(vmadc_vim, "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) -OP(vsbc_vvm, "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) -OP(vsbc_vxm, "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) -OP(vmsbc_vvm, "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) -OP(vmsbc_vxm, "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) -OP(vand_vv, "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vand_vx, "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vand_vi, "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vor_vv, "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vor_vx, "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vor_vi, "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vxor_vv, "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vxor_vx, "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vxor_vi, "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vsll_vv, "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vsll_vx, "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vsll_vi, "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vsrl_vv, "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vsrl_vx, "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vsrl_vi, "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vsra_vv, "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vsra_vx, "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vsra_vi, "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vnsrl_wv, "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vnsrl_wx, "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vnsrl_wi, "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vnsra_wv, "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vnsra_wx, "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vnsra_wi, "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vmseq_vv, "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmseq_vx, "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmseq_vi, "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vmsne_vv, "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmsne_vx, "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmsne_vi, "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vmsltu_vv, "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmsltu_vx, "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmslt_vv, "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmslt_vx, "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmsleu_vv, "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmsleu_vx, "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmsleu_vi, "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vmsle_vv, "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmsle_vx, "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmsle_vi, "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vmsgtu_vx, "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmsgtu_vi, "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vmsgt_vx, "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmsgt_vi, "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vminu_vv, "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vminu_vx, "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmin_vv, "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmin_vx, "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmaxu_vv, "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmaxu_vx, "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmax_vv, "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmax_vx, "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmul_vv, "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmul_vx, "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmulh_vv, "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmulh_vx, "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmulhu_vv, "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmulhu_vx, "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmulhsu_vv, "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmulhsu_vx, "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vdivu_vv, "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vdivu_vx, "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vdiv_vv, "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vdiv_vx, "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vremu_vv, "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vremu_vx, "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vrem_vv, "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vrem_vx, "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwmulu_vv, "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwmulu_vx, "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwmulsu_vv, "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwmulsu_vx, "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwmul_vv, "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwmul_vx, "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vmacc_vv, "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vmacc_vx, "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vnmsac_vv, "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vnmsac_vx, "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vmadd_vv, "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vmadd_vx, "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vnmsub_vv, "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vnmsub_vx, "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vwmaccu_vv, "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vwmaccu_vx, "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vwmacc_vv, "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vwmacc_vx, "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vwmaccsu_vv, "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vwmaccsu_vx, "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vwmaccus_vx, "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) -OP(vmv_v_v, "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1) -OP(vmv_v_x, "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1) -OP(vmv_v_i, "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm) -OP(vmerge_vvm, "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) -OP(vmerge_vxm, "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) -OP(vmerge_vim, "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) -OP(vsaddu_vv, "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vsaddu_vx, "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vsaddu_vi, "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vsadd_vv, "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vsadd_vx, "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vsadd_vi, "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) -OP(vssubu_vv, "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vssubu_vx, "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vssub_vv, "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vssub_vx, "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(uret, "uret", rv_codec_none, rv_fmt_none) OP(vaadd_vv, "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vaadd_vx, "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vaaddu_vv, "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vaaddu_vx, "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vadc_vim, "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vadc_vvm, "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vadc_vxm, "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vadd_vi, "vadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vadd_vv, "vadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vadd_vx, "vadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vaesdf_vs, "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdf_vv, "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdm_vs, "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesdm_vv, "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesef_vs, "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesef_vv, "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesem_vs, "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaesem_vv, "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vaeskf1_vi, "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vaeskf2_vi, "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) +OP(vaesz_vs, "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vand_vi, "vand.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vand_vv, "vand.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vand_vx, "vand.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vandn_vv, "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vandn_vx, "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vasub_vv, "vasub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vasub_vx, "vasub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vasubu_vv, "vasubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vasubu_vx, "vasubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vsmul_vv, "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vsmul_vx, "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vssrl_vv, "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vssrl_vx, "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vssrl_vi, "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vssra_vv, "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vssra_vx, "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vssra_vi, "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vnclipu_wv, "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vnclipu_wx, "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vnclipu_wi, "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vnclip_wv, "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vnclip_wx, "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vnclip_wi, "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vfadd_vv, "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfadd_vf, "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfsub_vv, "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfsub_vf, "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfrsub_vf, "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfwadd_vv, "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfwadd_vf, "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfwadd_wv, "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfwadd_wf, "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfwsub_vv, "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfwsub_vf, "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfwsub_wv, "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfwsub_wf, "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfmul_vv, "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfmul_vf, "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfdiv_vv, "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfdiv_vf, "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfrdiv_vf, "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfwmul_vv, "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfwmul_vf, "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfmacc_vv, "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfmacc_vf, "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfnmacc_vv, "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfnmacc_vf, "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfmsac_vv, "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfmsac_vf, "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfnmsac_vv, "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfnmsac_vf, "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfmadd_vv, "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfmadd_vf, "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfnmadd_vv, "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfnmadd_vf, "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfmsub_vv, "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfmsub_vf, "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfnmsub_vv, "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfnmsub_vf, "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfwmacc_vv, "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfwmacc_vf, "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfwnmacc_vv, "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfwnmacc_vf, "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfwmsac_vv, "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfwmsac_vf, "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfwnmsac_vv, "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfwnmsac_vf, "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(vfsqrt_v, "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vfrsqrt7_v, "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vfrec7_v, "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vfmin_vv, "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfmin_vf, "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfmax_vv, "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfmax_vf, "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfsgnj_vv, "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfsgnj_vf, "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfsgnjn_vv, "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfsgnjn_vf, "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfsgnjx_vv, "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfsgnjx_vf, "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfslide1up_vf, "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfslide1down_vf, "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vmfeq_vv, "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmfeq_vf, "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vmfne_vv, "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmfne_vf, "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vmflt_vv, "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmflt_vf, "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vmfle_vv, "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmfle_vf, "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vmfgt_vf, "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vmfge_vf, "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) -OP(vfclass_v, "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfmerge_vfm, "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl) -OP(vfmv_v_f, "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1) -OP(vfcvt_xu_f_v, "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfcvt_x_f_v, "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfcvt_f_xu_v, "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfcvt_f_x_v, "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfcvt_rtz_xu_f_v, "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfcvt_rtz_x_f_v, "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvt_xu_f_v, "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvt_x_f_v, "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvt_f_xu_v, "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvt_f_x_v, "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvt_f_f_v, "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvt_rtz_xu_f_v, "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvt_rtz_x_f_v, "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_xu_f_w, "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_x_f_w, "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_f_xu_w, "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_f_x_w, "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_f_f_w, "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_rod_f_f_w, "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_rtz_xu_f_w, "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfncvt_rtz_x_f_w, "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vredsum_vs, "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vredand_vs, "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vredor_vs, "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vredxor_vs, "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vredminu_vs, "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vredmin_vs, "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vredmaxu_vs, "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vredmax_vs, "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwredsumu_vs, "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vwredsum_vs, "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfredusum_vs, "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfredosum_vs, "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfredmin_vs, "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfredmax_vs, "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfwredusum_vs, "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vfwredosum_vs, "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmand_mm, "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmnand_mm, "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmandn_mm, "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmxor_mm, "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmor_mm, "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmnor_mm, "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmorn_mm, "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vmxnor_mm, "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vcpop_m, "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) -OP(vfirst_m, "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) -OP(vmsbf_m, "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vmsif_m, "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vmsof_m, "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(viota_m, "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vid_v, "vid.v", rv_codec_v_r, rv_fmt_vd_vm) -OP(vmv_x_s, "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2) -OP(vmv_s_x, "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1) -OP(vfmv_f_s, "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2) -OP(vfmv_s_f, "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1) -OP(vslideup_vx, "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vslideup_vi, "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vslide1up_vx, "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vslidedown_vx, "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vslidedown_vi, "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vslide1down_vx, "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vrgather_vv, "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vrgatherei16_vv, "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vrgather_vx, "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vrgather_vi, "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(vcompress_vm, "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1) -OP(vmv1r_v, "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vmv2r_v, "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vmv4r_v, "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vmv8r_v, "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vzext_vf2, "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vzext_vf4, "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vzext_vf8, "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vsext_vf2, "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vsext_vf4, "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vsext_vf8, "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vsetvli, "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli) -OP(vsetivli, "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli) -OP(vsetvl, "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(c_zext_b, "c.zext.b", rv_codec_zcb_ext, rv_fmt_rd) -OP(c_sext_b, "c.sext.b", rv_codec_zcb_ext, rv_fmt_rd) -OP(c_zext_h, "c.zext.h", rv_codec_zcb_ext, rv_fmt_rd) -OP(c_sext_h, "c.sext.h", rv_codec_zcb_ext, rv_fmt_rd) -OP(c_zext_w, "c.zext.w", rv_codec_zcb_ext, rv_fmt_rd) -OP(c_not, "c.not", rv_codec_zcb_ext, rv_fmt_rd) -OP(c_mul, "c.mul", rv_codec_zcb_mul, rv_fmt_rd_rs2) -OP(c_lbu, "c.lbu", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) -OP(c_lhu, "c.lhu", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) -OP(c_lh, "c.lh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) -OP(c_sb, "c.sb", rv_codec_zcb_lb, rv_fmt_rs1_rs2_zce_ldst) -OP(c_sh, "c.sh", rv_codec_zcb_lh, rv_fmt_rs1_rs2_zce_ldst) -OP(cm_push, "cm.push", rv_codec_zcmp_cm_pushpop, rv_fmt_push_rlist) -OP(cm_pop, "cm.pop", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) -OP(cm_popret, "cm.popret", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) -OP(cm_popretz, "cm.popretz", rv_codec_zcmp_cm_pushpop, rv_fmt_pop_rlist) -OP(cm_mva01s, "cm.mva01s", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) -OP(cm_mvsa01, "cm.mvsa01", rv_codec_zcmp_cm_mv, rv_fmt_rd_rs2) -OP(cm_jt, "cm.jt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) -OP(cm_jalt, "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index) -OP(czero_eqz, "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(czero_nez, "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2) -OP(fcvt_bf16_s, "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fcvt_s_bf16, "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(vfncvtbf16_f_f_w, "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwcvtbf16_f_f_v, "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) -OP(vfwmaccbf16_vv, "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) -OP(vfwmaccbf16_vf, "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) -OP(flh, "flh", rv_codec_i, rv_fmt_frd_offset_rs1) -OP(fsh, "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1) -OP(fmv_h_x, "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1) -OP(fmv_x_h, "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1) -OP(fli_s, "fli.s", rv_codec_fli, rv_fmt_fli) -OP(fli_d, "fli.d", rv_codec_fli, rv_fmt_fli) -OP(fli_q, "fli.q", rv_codec_fli, rv_fmt_fli) -OP(fli_h, "fli.h", rv_codec_fli, rv_fmt_fli) -OP(fminm_s, "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fmaxm_s, "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fminm_d, "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fmaxm_d, "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fminm_q, "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fmaxm_q, "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fminm_h, "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fmaxm_h, "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2) -OP(fround_s, "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(froundnx_s, "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fround_d, "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(froundnx_d, "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fround_q, "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(froundnx_q, "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fround_h, "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(froundnx_h, "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1) -OP(fcvtmod_w_d, "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1) -OP(fmvh_x_d, "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1) -OP(fmvp_d_x, "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2) -OP(fmvh_x_q, "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1) -OP(fmvp_q_x, "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2) -OP(fleq_s, "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fltq_s, "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fleq_d, "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fltq_d, "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fleq_q, "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fltq_q, "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fleq_h, "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(fltq_h, "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2) -OP(vaesdf_vv, "vaesdf.vv", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaesdf_vs, "vaesdf.vs", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaesdm_vv, "vaesdm.vv", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaesdm_vs, "vaesdm.vs", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaesef_vv, "vaesef.vv", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaesef_vs, "vaesef.vs", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaesem_vv, "vaesem.vv", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaesem_vs, "vaesem.vs", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vaeskf1_vi, "vaeskf1.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) -OP(vaeskf2_vi, "vaeskf2.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) -OP(vaesz_vs, "vaesz.vs", rv_codec_v_r, rv_fmt_vd_vs2) -OP(vandn_vv, "vandn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vandn_vx, "vandn.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vbrev_v, "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) OP(vbrev8_v, "vbrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vbrev_v, "vbrev.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) OP(vclmul_vv, "vclmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vclmul_vx, "vclmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vclmulh_vv, "vclmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vclmulh_vx, "vclmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vclz_v, "vclz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vcompress_vm, "vcompress.vm", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vcpop_m, "vcpop.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) OP(vcpop_v, "vcpop.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) OP(vctz_v, "vctz.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vdiv_vv, "vdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vdiv_vx, "vdiv.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vdivu_vv, "vdivu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vdivu_vx, "vdivu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vfadd_vf, "vfadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfadd_vv, "vfadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfclass_v, "vfclass.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_f_x_v, "vfcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_f_xu_v, "vfcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_rtz_x_f_v, "vfcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_rtz_xu_f_v, "vfcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_x_f_v, "vfcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfcvt_xu_f_v, "vfcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfdiv_vf, "vfdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfdiv_vv, "vfdiv.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfirst_m, "vfirst.m", rv_codec_v_r, rv_fmt_rd_vs2_vm) +OP(vfmacc_vf, "vfmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmacc_vv, "vfmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmadd_vf, "vfmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmadd_vv, "vfmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmax_vf, "vfmax.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmax_vv, "vfmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmerge_vfm, "vfmerge.vfm", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vl) +OP(vfmin_vf, "vfmin.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmin_vv, "vfmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmsac_vf, "vfmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmsac_vv, "vfmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmsub_vf, "vfmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfmsub_vv, "vfmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfmul_vf, "vfmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfmul_vv, "vfmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfmv_f_s, "vfmv.f.s", rv_codec_v_r, rv_fmt_fd_vs2) +OP(vfmv_s_f, "vfmv.s.f", rv_codec_v_r, rv_fmt_vd_fs1) +OP(vfmv_v_f, "vfmv.v.f", rv_codec_v_r, rv_fmt_vd_fs1) +OP(vfncvt_f_f_w, "vfncvt.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_f_x_w, "vfncvt.f.x.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_f_xu_w, "vfncvt.f.xu.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rod_f_f_w, "vfncvt.rod.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rtz_x_f_w, "vfncvt.rtz.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_rtz_xu_f_w, "vfncvt.rtz.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_x_f_w, "vfncvt.x.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvt_xu_f_w, "vfncvt.xu.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfncvtbf16_f_f_w, "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfnmacc_vf, "vfnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmacc_vv, "vfnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmadd_vf, "vfnmadd.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmadd_vv, "vfnmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmsac_vf, "vfnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmsac_vv, "vfnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfnmsub_vf, "vfnmsub.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfnmsub_vv, "vfnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfrdiv_vf, "vfrdiv.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfrec7_v, "vfrec7.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfredmax_vs, "vfredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredmin_vs, "vfredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredosum_vs, "vfredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfredusum_vs, "vfredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfrsqrt7_v, "vfrsqrt7.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfrsub_vf, "vfrsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnj_vf, "vfsgnj.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnj_vv, "vfsgnj.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsgnjn_vf, "vfsgnjn.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnjn_vv, "vfsgnjn.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfsgnjx_vf, "vfsgnjx.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsgnjx_vv, "vfsgnjx.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfslide1down_vf, "vfslide1down.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfslide1up_vf, "vfslide1up.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsqrt_v, "vfsqrt.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vfsub_vf, "vfsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfsub_vv, "vfsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwadd_vf, "vfwadd.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwadd_vv, "vfwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwadd_wf, "vfwadd.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwadd_wv, "vfwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwcvt_f_f_v, "vfwcvt.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_f_x_v, "vfwcvt.f.x.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_f_xu_v, "vfwcvt.f.xu.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_rtz_x_f_v, "vfwcvt.rtz.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_rtz_xu_f_v, "vfwcvt.rtz.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_x_f_v, "vfwcvt.x.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvt_xu_f_v, "vfwcvt.xu.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwcvtbf16_f_f_v, "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vfwmacc_vf, "vfwmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmacc_vv, "vfwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmaccbf16_vf, "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmaccbf16_vv, "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmsac_vf, "vfwmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwmsac_vv, "vfwmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwmul_vf, "vfwmul.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwmul_vv, "vfwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwnmacc_vf, "vfwnmacc.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwnmacc_vv, "vfwnmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwnmsac_vf, "vfwnmsac.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm) +OP(vfwnmsac_vv, "vfwnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vfwredosum_vs, "vfwredosum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwredusum_vs, "vfwredusum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwsub_vf, "vfwsub.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwsub_vv, "vfwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vfwsub_wf, "vfwsub.wf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vfwsub_wv, "vfwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vghsh_vv, "vghsh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) OP(vgmul_vv, "vgmul.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vid_v, "vid.v", rv_codec_v_r, rv_fmt_vd_vm) +OP(viota_m, "viota.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vl1re16_v, "vl1re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re32_v, "vl1re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re64_v, "vl1re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl1re8_v, "vl1re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re16_v, "vl2re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re32_v, "vl2re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re64_v, "vl2re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl2re8_v, "vl2re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re16_v, "vl4re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re32_v, "vl4re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re64_v, "vl4re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl4re8_v, "vl4re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re16_v, "vl8re16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re32_v, "vl8re32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re64_v, "vl8re64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vl8re8_v, "vl8re8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle16_v, "vle16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle16ff_v, "vle16ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle32_v, "vle32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle32ff_v, "vle32ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle64_v, "vle64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle64ff_v, "vle64ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle8_v, "vle8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vle8ff_v, "vle8ff.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vlm_v, "vlm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vloxei16_v, "vloxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei32_v, "vloxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei64_v, "vloxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vloxei8_v, "vloxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vlse16_v, "vlse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse32_v, "vlse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse64_v, "vlse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vlse8_v, "vlse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vluxei16_v, "vluxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei32_v, "vluxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei64_v, "vluxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vluxei8_v, "vluxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vmacc_vv, "vmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vmacc_vx, "vmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vmadc_vim, "vmadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vmadc_vvm, "vmadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmadc_vxm, "vmadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmadd_vv, "vmadd.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vmadd_vx, "vmadd.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vmand_mm, "vmand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmandn_mm, "vmandn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmax_vv, "vmax.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmax_vx, "vmax.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmaxu_vv, "vmaxu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmaxu_vx, "vmaxu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmerge_vim, "vmerge.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) +OP(vmerge_vvm, "vmerge.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmerge_vxm, "vmerge.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmfeq_vf, "vmfeq.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfeq_vv, "vmfeq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmfge_vf, "vmfge.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfgt_vf, "vmfgt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfle_vf, "vmfle.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfle_vv, "vmfle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmflt_vf, "vmflt.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmflt_vv, "vmflt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmfne_vf, "vmfne.vf", rv_codec_v_r, rv_fmt_vd_vs2_fs1_vm) +OP(vmfne_vv, "vmfne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmin_vv, "vmin.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmin_vx, "vmin.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vminu_vv, "vminu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vminu_vx, "vminu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmnand_mm, "vmnand.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmnor_mm, "vmnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmor_mm, "vmor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmorn_mm, "vmorn.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsbc_vvm, "vmsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vmsbc_vxm, "vmsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vmsbf_m, "vmsbf.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmseq_vi, "vmseq.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmseq_vv, "vmseq.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmseq_vx, "vmseq.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsgt_vi, "vmsgt.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsgt_vx, "vmsgt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsgtu_vi, "vmsgtu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsgtu_vx, "vmsgtu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsif_m, "vmsif.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmsle_vi, "vmsle.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsle_vv, "vmsle.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsle_vx, "vmsle.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsleu_vi, "vmsleu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsleu_vv, "vmsleu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsleu_vx, "vmsleu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmslt_vv, "vmslt.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmslt_vx, "vmslt.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsltu_vv, "vmsltu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsltu_vx, "vmsltu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsne_vi, "vmsne.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vmsne_vv, "vmsne.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmsne_vx, "vmsne.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmsof_m, "vmsof.m", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vmul_vv, "vmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmul_vx, "vmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulh_vv, "vmulh.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulh_vx, "vmulh.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulhsu_vv, "vmulhsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulhsu_vx, "vmulhsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmulhu_vv, "vmulhu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmulhu_vx, "vmulhu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vmv1r_v, "vmv1r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv2r_v, "vmv2r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv4r_v, "vmv4r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv8r_v, "vmv8r.v", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vmv_s_x, "vmv.s.x", rv_codec_v_r, rv_fmt_vd_rs1) +OP(vmv_v_i, "vmv.v.i", rv_codec_v_i, rv_fmt_vd_imm) +OP(vmv_v_v, "vmv.v.v", rv_codec_v_r, rv_fmt_vd_vs1) +OP(vmv_v_x, "vmv.v.x", rv_codec_v_r, rv_fmt_vd_rs1) +OP(vmv_x_s, "vmv.x.s", rv_codec_v_r, rv_fmt_rd_vs2) +OP(vmxnor_mm, "vmxnor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vmxor_mm, "vmxor.mm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclip_wi, "vnclip.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnclip_wv, "vnclip.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclip_wx, "vnclip.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnclipu_wi, "vnclipu.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnclipu_wv, "vnclipu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnclipu_wx, "vnclipu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnmsac_vv, "vnmsac.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vnmsac_vx, "vnmsac.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vnmsub_vv, "vnmsub.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vnmsub_vx, "vnmsub.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vnsra_wi, "vnsra.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnsra_wv, "vnsra.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnsra_wx, "vnsra.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vnsrl_wi, "vnsrl.wi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vnsrl_wv, "vnsrl.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vnsrl_wx, "vnsrl.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vor_vi, "vor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vor_vv, "vor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vor_vx, "vor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vredand_vs, "vredand.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmax_vs, "vredmax.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmaxu_vs, "vredmaxu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredmin_vs, "vredmin.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredminu_vs, "vredminu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredor_vs, "vredor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredsum_vs, "vredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vredxor_vs, "vredxor.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrem_vv, "vrem.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrem_vx, "vrem.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vremu_vv, "vremu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vremu_vx, "vremu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vrev8_v, "vrev8.v", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vrgather_vi, "vrgather.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vrgather_vv, "vrgather.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vrgather_vx, "vrgather.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vrgatherei16_vv, "vrgatherei16.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vrol_vv, "vrol.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vrol_vx, "vrol.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vror_vi, "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm) OP(vror_vv, "vror.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vror_vx, "vror.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vror_vi, "vror.vi", rv_codec_vror_vi, rv_fmt_vd_vs2_uimm_vm) +OP(vrsub_vi, "vrsub.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vrsub_vx, "vrsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vs1r_v, "vs1r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs2r_v, "vs2r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs4r_v, "vs4r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vs8r_v, "vs8r.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vsadd_vi, "vsadd.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vsadd_vv, "vsadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsadd_vx, "vsadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsaddu_vi, "vsaddu.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vsaddu_vv, "vsaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsaddu_vx, "vsaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsbc_vvm, "vsbc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) +OP(vsbc_vxm, "vsbc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) +OP(vse16_v, "vse16.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse32_v, "vse32.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse64_v, "vse64.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vse8_v, "vse8.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vsetivli, "vsetivli", rv_codec_vsetivli, rv_fmt_vsetivli) +OP(vsetvl, "vsetvl", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(vsetvli, "vsetvli", rv_codec_vsetvli, rv_fmt_vsetvli) +OP(vsext_vf2, "vsext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsext_vf4, "vsext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vsext_vf8, "vsext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) OP(vsha2ch_vv, "vsha2ch.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) OP(vsha2cl_vv, "vsha2cl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) OP(vsha2ms_vv, "vsha2ms.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) +OP(vslide1down_vx, "vslide1down.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslide1up_vx, "vslide1up.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslidedown_vi, "vslidedown.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vslidedown_vx, "vslidedown.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vslideup_vi, "vslideup.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vslideup_vx, "vslideup.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsll_vi, "vsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsll_vv, "vsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsll_vx, "vsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vsm3c_vi, "vsm3c.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) OP(vsm3me_vv, "vsm3me.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1) OP(vsm4k_vi, "vsm4k.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm) -OP(vsm4r_vv, "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2) OP(vsm4r_vs, "vsm4r.vs", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vsm4r_vv, "vsm4r.vv", rv_codec_v_r, rv_fmt_vd_vs2) +OP(vsm_v, "vsm.v", rv_codec_v_ldst, rv_fmt_ldst_vd_rs1_vm) +OP(vsmul_vv, "vsmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsmul_vx, "vsmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsoxei16_v, "vsoxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei32_v, "vsoxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei64_v, "vsoxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsoxei8_v, "vsoxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsra_vi, "vsra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsra_vv, "vsra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsra_vx, "vsra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsrl_vi, "vsrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vsrl_vv, "vsrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsrl_vx, "vsrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsse16_v, "vsse16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse32_v, "vsse32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse64_v, "vsse64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vsse8_v, "vsse8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_rs2_vm) +OP(vssra_vi, "vssra.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vssra_vv, "vssra.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssra_vx, "vssra.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssrl_vi, "vssrl.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) +OP(vssrl_vv, "vssrl.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssrl_vx, "vssrl.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssub_vv, "vssub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssub_vx, "vssub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vssubu_vv, "vssubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vssubu_vx, "vssubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsub_vv, "vsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vsub_vx, "vsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vsuxei16_v, "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei32_v, "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei64_v, "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vsuxei8_v, "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vwadd_vv, "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwadd_vx, "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwadd_wv, "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwadd_wx, "vwadd.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwaddu_vv, "vwaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwaddu_vx, "vwaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwaddu_wv, "vwaddu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwaddu_wx, "vwaddu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmacc_vv, "vwmacc.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmacc_vx, "vwmacc.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccsu_vv, "vwmaccsu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmaccsu_vx, "vwmaccsu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccu_vv, "vwmaccu.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm) +OP(vwmaccu_vx, "vwmaccu.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmaccus_vx, "vwmaccus.vx", rv_codec_v_r, rv_fmt_vd_rs1_vs2_vm) +OP(vwmul_vv, "vwmul.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmul_vx, "vwmul.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmulsu_vv, "vwmulsu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmulsu_vx, "vwmulsu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwmulu_vv, "vwmulu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwmulu_vx, "vwmulu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwredsum_vs, "vwredsum.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwredsumu_vs, "vwredsumu.vs", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsll_vi, "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) OP(vwsll_vv, "vwsll.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vwsll_vx, "vwsll.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) -OP(vwsll_vi, "vwsll.vi", rv_codec_v_i_u, rv_fmt_vd_vs2_uimm_vm) -OP(amocas_w, "amocas.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amocas_d, "amocas.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amocas_q, "amocas.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(mop_r, "mop.r", rv_codec_mop_r, rv_fmt_mop_r) -OP(mop_rr, "mop.rr", rv_codec_mop_rr, rv_fmt_mop_rr) -OP(c_mop, "c.mop", rv_codec_cmop, rv_fmt_cmop) -OP(amoswap_b, "amoswap.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoadd_b, "amoadd.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoxor_b, "amoxor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoor_b, "amoor.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoand_b, "amoand.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomin_b, "amomin.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomax_b, "amomax.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amominu_b, "amominu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomaxu_b, "amomaxu.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoswap_h, "amoswap.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoadd_h, "amoadd.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoxor_h, "amoxor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoor_h, "amoor.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amoand_h, "amoand.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomin_h, "amomin.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomax_h, "amomax.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amominu_h, "amominu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amomaxu_h, "amomaxu.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amocas_b, "amocas.b", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(amocas_h, "amocas.h", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(wrs_sto, "wrs.sto", rv_codec_none, rv_fmt_none) +OP(vwsub_vv, "vwsub.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsub_vx, "vwsub.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsub_wv, "vwsub.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsub_wx, "vwsub.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsubu_vv, "vwsubu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsubu_vx, "vwsubu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwsubu_wv, "vwsubu.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwsubu_wx, "vwsubu.wx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vxor_vi, "vxor.vi", rv_codec_v_i, rv_fmt_vd_vs2_imm_vm) +OP(vxor_vv, "vxor.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vxor_vx, "vxor.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vzext_vf2, "vzext.vf2", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vzext_vf4, "vzext.vf4", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(vzext_vf8, "vzext.vf8", rv_codec_v_r, rv_fmt_vd_vs2_vm) +OP(wfi, "wfi", rv_codec_none, rv_fmt_none) OP(wrs_nto, "wrs.nto", rv_codec_none, rv_fmt_none) -OP(lpad, "lpad", rv_codec_lp, rv_fmt_imm) -OP(sspush, "sspush", rv_codec_r, rv_fmt_rs2) -OP(sspopchk, "sspopchk", rv_codec_r, rv_fmt_rs1) -OP(ssrdp, "ssrdp", rv_codec_r, rv_fmt_rd) -OP(ssamoswap_w, "ssamoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(ssamoswap_d, "ssamoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1) -OP(c_sspush, "c.sspush", rv_codec_cmop_ss, NULL, DECOMP(op_sspush)) -OP(c_sspopchk, "c.sspopchk", rv_codec_cmop_ss, NULL, DECOMP(op_sspopchk)) -OP(cbo_inval, "cbo.inval", rv_codec_r, rv_fmt_rs1) -OP(cbo_clean, "cbo.clean", rv_codec_r, rv_fmt_rs1) -OP(cbo_flush, "cbo.flush", rv_codec_r, rv_fmt_rs1) -OP(cbo_zero, "cbo.zero", rv_codec_r, rv_fmt_rs1) -OP(mnret, "mnret", rv_codec_none, rv_fmt_none) +OP(wrs_sto, "wrs.sto", rv_codec_none, rv_fmt_none) +OP(xnor, "xnor", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(xor, "xor", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(xori, "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori) +OP(xperm4, "xperm4", rv_codec_r, rv_fmt_rd_rs1_rs2) +OP(xperm8, "xperm8", rv_codec_r, rv_fmt_rd_rs1) +OP(zext_h, "zext.h", rv_codec_r, rv_fmt_rd_rs1) +OP(zip, "zip", rv_codec_r, rv_fmt_rd_rs1) From 12289f2f9a0f1ffa6a75c2b189a0273dc455dfb0 Mon Sep 17 00:00:00 2001 From: wangyang Date: Thu, 13 Aug 2026 18:39:06 +0800 Subject: [PATCH 801/860] target/riscv: honor zicbo* envcfg gating in linux-user mode In user-only builds check_zicbo_envcfg() skipped the envcfg check entirely (#if !defined(CONFIG_USER_ONLY)), so cbo.inval/cbo.flush/ cbo.zero retired unconditionally in linux-user even though the machine-level envcfg fields are never initialized. Give the user-mode build a senvcfg-based gate, and initialize SENVCFG_CBZE at reset when ext_zicboz is enabled so cbo.zero stays usable while cbo.inval/cbo.flush remain illegal, matching the user-mode view of a typical firmware/kernel setup. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4107 Reviewed-by: Alistair Francis Signed-off-by: wangyang Message-ID: <9b2f22fc402b48b8ba81f72be8ed04bc@wangyang25.otcaix.iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 8 ++++++++ target/riscv/tcg/op_helper.c | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d8f239e41d..79ddde419a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1062,6 +1062,14 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) #else env->priv = PRV_U; env->senvcfg = 0; + /* + * Match the user-mode view of a typical firmware/kernel setup where + * cbo.zero is enabled for user mode; the CBCFE/CBIE bits stay zero, + * so the cache-management operations remain illegal in user mode. + */ + if (riscv_cpu_cfg(env)->ext_zicboz) { + env->senvcfg |= SENVCFG_CBZE; + } env->menvcfg = 0; #endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c index acdccd1a97..a7bd42d2c6 100644 --- a/target/riscv/tcg/op_helper.c +++ b/target/riscv/tcg/op_helper.c @@ -152,7 +152,16 @@ target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, static void check_zicbo_envcfg(CPURISCVState *env, target_ulong envbits, uintptr_t ra) { -#ifndef CONFIG_USER_ONLY +#if defined(CONFIG_USER_ONLY) + /* + * linux-user: the machine-level envcfg fields are not part of the + * user-mode environment; only the user-mode view of the enabling + * bits (senvcfg, as initialized for the guest) applies. + */ + if (!get_field(env->senvcfg, envbits)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); + } +#else if ((env->priv < PRV_M) && !get_field(env->menvcfg, envbits)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); } From af1e669cef162e8d9b5f2f176e8b909d61c4b530 Mon Sep 17 00:00:00 2001 From: wangyang Date: Thu, 13 Aug 2026 18:39:06 +0800 Subject: [PATCH 802/860] target/riscv: reject FMV.X.W/FMV.W.X under Zfinx Zfinx explicitly excludes the FMV transfer instructions, but trans_fmv_x_w/trans_fmv_w_x used REQUIRE_ZFINX_OR_F so a Zfinx-only CPU accepted them. Require RVF instead so the transfers trap with an illegal instruction when only Zfinx is present. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4108 Reviewed-by: Alistair Francis Signed-off-by: wangyang Message-ID: <36c7cfebd27b4b6e8bcdd00e09e9dda0@wangyang25.otcaix.iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/tcg/insn_trans/trans_rvf.c.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/tcg/insn_trans/trans_rvf.c.inc b/target/riscv/tcg/insn_trans/trans_rvf.c.inc index 481f7282da..636dec1664 100644 --- a/target/riscv/tcg/insn_trans/trans_rvf.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvf.c.inc @@ -434,7 +434,8 @@ static bool trans_fmv_x_w(DisasContext *ctx, arg_fmv_x_w *a) { /* NOTE: This was FMV.X.S in an earlier version of the ISA spec! */ REQUIRE_FPU; - REQUIRE_ZFINX_OR_F(ctx); + /* Zfinx explicitly excludes the FMV transfer instructions. */ + REQUIRE_EXT(ctx, RVF); TCGv dest = dest_gpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); @@ -537,7 +538,8 @@ static bool trans_fmv_w_x(DisasContext *ctx, arg_fmv_w_x *a) { /* NOTE: This was FMV.S.X in an earlier version of the ISA spec! */ REQUIRE_FPU; - REQUIRE_ZFINX_OR_F(ctx); + /* Zfinx explicitly excludes the FMV transfer instructions. */ + REQUIRE_EXT(ctx, RVF); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO); From e68cc2e5cd28333ac76d757ed2db7d2f7635d09a Mon Sep 17 00:00:00 2001 From: wangyang Date: Thu, 13 Aug 2026 18:39:06 +0800 Subject: [PATCH 803/860] target/riscv: enforce even register constraints for Zdinx fcvt pairs fcvt.d.h and fcvt.h.d access a 64-bit double held in a register pair, so under Zdinx/Zhinxmin the odd-rd (fcvt.d.h) and odd-rs1 (fcvt.h.d) encodings are reserved. Add the missing REQUIRE_EVEN checks so those encodings raise an illegal-instruction exception instead of retiring. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4109 Reviewed-by: Alistair Francis Signed-off-by: wangyang Message-ID: Signed-off-by: Alistair Francis --- target/riscv/tcg/insn_trans/trans_rvzfh.c.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc b/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc index 88711fb076..b17ef9541c 100644 --- a/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvzfh.c.inc @@ -419,6 +419,7 @@ static bool trans_fcvt_d_h(DisasContext *ctx, arg_fcvt_d_h *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rd); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1); @@ -453,6 +454,7 @@ static bool trans_fcvt_h_d(DisasContext *ctx, arg_fcvt_h_d *a) REQUIRE_FPU; REQUIRE_ZFHMIN_OR_ZHINXMIN(ctx); REQUIRE_ZDINX_OR_D(ctx); + REQUIRE_EVEN(ctx, a->rs1); TCGv_i64 dest = dest_fpr(ctx, a->rd); TCGv_i64 src1 = get_fpr_d(ctx, a->rs1); From e4ae434836a98039ed0511dea506992b55e55865 Mon Sep 17 00:00:00 2001 From: Chao Liu Date: Thu, 13 Aug 2026 13:43:29 +0800 Subject: [PATCH 804/860] tests/qtest: remove trace output from k230 watchdog test The interrupt mode test does not depend on trace output, but passes a trace file to QEMU. This makes startup fail when QEMU is built without the log or simple trace backend, so qtest cannot connect. Drop the unnecessary trace option. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4126 Signed-off-by: Chao Liu Reviewed-by: Bin Meng Message-ID: <20260813054329.35425-1-chao.liu@processmission.com> Signed-off-by: Alistair Francis --- tests/qtest/k230-wdt-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/k230-wdt-test.c b/tests/qtest/k230-wdt-test.c index c8eaeaf1ae..b1131f42e3 100644 --- a/tests/qtest/k230-wdt-test.c +++ b/tests/qtest/k230-wdt-test.c @@ -76,7 +76,7 @@ static void test_counter_restart(void) static void test_interrupt_mode(void) { - QTestState *qts = qtest_init("-machine k230 --trace k230_*,file=k230.log"); + QTestState *qts = qtest_init("-machine k230"); /* Set interrupt mode and enable watchdog */ qtest_writel(qts, WDT_BASE + K230_WDT_CR, From 409c7d8682ae91c5e275d744d55a2ac19789534a Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Thu, 13 Aug 2026 12:54:14 +0930 Subject: [PATCH 805/860] target/riscv: Restore register dump zero padding The register values lost their leading zeroes when the underlying type was changed, resulting in mismatched padding and harder to read output. Print with a runtime field width based on MXL, so values are 16 hex digits on rv64 and 8 on rv32, matching the csr and fp dump. This avoids adding target_ulong back into the dump. Fixes: c4e6bc63853c ("target/riscv: Fix size of gpr and gprh") Signed-off-by: Joel Stanley Reviewed-by: Anton Johansson Reviewed-by: Max Chou Message-ID: <20260813032421.54438-1-joel@jms.id.au> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 79ddde419a..a13177b7b7 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -651,6 +651,9 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; + bool rv32 = riscv_cpu_is_32bit(cpu); + int width = rv32 ? 8 : 16; + uint64_t mask = rv32 ? UINT32_MAX : UINT64_MAX; int i, j; uint8_t *p; @@ -665,7 +668,7 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, " %-13s %d\n", "elp", env->elp); } #endif - qemu_fprintf(f, " %-13s %" PRIx64 "\n", "pc", env->pc); + qemu_fprintf(f, " %-13s %0*" PRIx64 "\n", "pc", width, env->pc & mask); #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) for (i = 0; i < ARRAY_SIZE(csr_ops); i++) { int csrno = i; @@ -692,8 +695,8 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags) #endif for (i = 0; i < 32; i++) { - qemu_fprintf(f, " %-8s %" PRIx64, - riscv_int_regnames[i], env->gpr[i]); + qemu_fprintf(f, " %-8s %0*" PRIx64, + riscv_int_regnames[i], width, env->gpr[i] & mask); if ((i & 3) == 3) { qemu_fprintf(f, "\n"); } From ea8e5d3c504d38a6eb82a338dc48b9ba7b072153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9trot?= Date: Wed, 19 Aug 2026 12:56:54 +0200 Subject: [PATCH 806/860] target/riscv: Fix sstatus update in rv128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sstatus register assignment was performed before the write mask was fully constructed, leading to an incomplete update of sstatus fields on the experimental rv128 target. Move the sstatus write after the mask completion so the full write mask is applied correctly. Signed-off-by: Frédéric Pétrot Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Chao Liu Message-ID: <20260819105655.33391-2-frederic.petrot@univ-grenoble-alpes.fr> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index ffd751caa5..9cc6297328 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -3998,7 +3998,7 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, Int128 *val) { uint64_t mask = sstatus_v1_10_mask; - uint64_t sstatus = env->mstatus & mask; + uint64_t sstatus; if (env->xl != MXL_RV32 || env->debugger) { mask |= SSTATUS64_UXL; } @@ -4009,7 +4009,7 @@ static RISCVException read_sstatus_i128(CPURISCVState *env, int csrno, if (env_archcpu(env)->cfg.ext_zicfilp) { mask |= SSTATUS_SPELP; } - + sstatus = env->mstatus & mask; *val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus)); return RISCV_EXCP_NONE; } From a9779d24be31338d90aa19a560e69cc4c9660627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9trot?= Date: Wed, 19 Aug 2026 12:56:55 +0200 Subject: [PATCH 807/860] target/riscv: Allow UXL to be 3 in mstatus on rv128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Valid UXL field values for mstatus were restricted to fix a reported issue, but this inadvertently broke the experimental rv128 support where a value of 3 validly represents 128-bit execution. Update the mstatus write logic to permit UXL=3 when running on an rv128 CPU. Fixes: dcd028517749 ("target/riscv: Apply UXL WARL handling to vsstatus") Signed-off-by: Frédéric Pétrot Reviewed-by: Daniel Henrique Barboza Reviewed-by: Chao Liu Message-ID: <20260819105655.33391-3-frederic.petrot@univ-grenoble-alpes.fr> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index 9cc6297328..4c8959a33d 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -2014,8 +2014,8 @@ static uint64_t riscv_write_uxl(CPURISCVState *env, uint64_t val, RISCVMXL xl = riscv_cpu_mxl(env); uint64_t uxl = get_field(val, field); - if (uxl == MXL_RV128) { - uxl = xl == MXL_RV128 ? MXL_RV64 : xl; + if (xl != MXL_RV128 && uxl == MXL_RV128) { + uxl = xl; val = set_field(val, field, uxl); } From af37cd2821f970a1a9d2d34fa0d319f80d2ab84d Mon Sep 17 00:00:00 2001 From: Vladimir Isaev Date: Mon, 17 Aug 2026 18:06:37 +0300 Subject: [PATCH 808/860] riscv: csr: do not drop C bit on misa write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to spec: > Writing misa may increase IALIGN, e.g., by disabling the "C" extension. > If an instruction that would write misa increases IALIGN, and the > subsequent instruction’s address is not IALIGN-bit aligned, the > write to misa is suppressed, leaving misa unchanged. So attempt to disable C extension if next instruction is not aligned should not change the misa. Reviewed-by: Alistair Francis Signed-off-by: Vladimir Isaev Reviewed-by: Chao Liu Message-ID: <20260817150653.40357-2-vvisaev@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index 4c8959a33d..002f7e69c1 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -2187,9 +2187,13 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, /* Mask extensions that are not supported by this hart */ val &= env->misa_ext_mask; - /* Suppress 'C' if next instruction is not aligned. */ - if ((val & RVC) && (get_next_pc(env, ra) & 3) != 0) { - val &= ~RVC; + /* drop write if RVC is cleared and next instruction is not aligned */ + if ((env->misa_ext & RVC) && !(val & RVC) && + (get_next_pc(env, ra) & 3) != 0) { + qemu_log_mask(LOG_GUEST_ERROR, "Unable to write MISA ext value " + "0x%x, MISA.C disable failed\n", env->misa_ext); + + return RISCV_EXCP_NONE; } /* Disable RVG if any of its dependencies are disabled */ From ce781055ff61308df9c4f9546c72e09b6908fe06 Mon Sep 17 00:00:00 2001 From: Vladimir Isaev Date: Mon, 17 Aug 2026 18:06:38 +0300 Subject: [PATCH 809/860] tests/tcg/riscv64: add misa write test Link: https://lore.kernel.org/r/20260321144554.606417-2-npiggin@gmail.com Suggested-by: Nicholas Piggin Signed-off-by: Vladimir Isaev Message-ID: <20260817150653.40357-3-vvisaev@gmail.com> Signed-off-by: Alistair Francis --- tests/tcg/riscv64/Makefile.softmmu-target | 4 ++ tests/tcg/riscv64/test-misa-w.S | 88 +++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/tcg/riscv64/test-misa-w.S diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index fac0474746..6a219c306c 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -67,5 +67,9 @@ EXTRA_RUNS += run-test-zicclsm-off run-test-zicclsm-off: test-zicclsm-off $(call run-test, $<, $(QEMU) -cpu rv64$(comma)v=true$(comma)zfh=true$(comma)zicclsm=false $(QEMU_OPTS)$<) +EXTRA_RUNS += run-test-misa-w +run-test-misa-w: test-misa-w + $(call run-test, $<, $(QEMU) -cpu rv64$(comma)x-misa-w=true$(comma)c=true$(comma)v=true $(QEMU_OPTS)$<) + # We don't currently support the multiarch system tests undefine MULTIARCH_TESTS diff --git a/tests/tcg/riscv64/test-misa-w.S b/tests/tcg/riscv64/test-misa-w.S new file mode 100644 index 0000000000..7f1eb30023 --- /dev/null +++ b/tests/tcg/riscv64/test-misa-w.S @@ -0,0 +1,88 @@ +/* + * Test for MISA changing C and related IALIGN alignment cases + * + * This test verifies that the "C" extension can be cleared and set in MISA, + * that a branch to 2-byte aligned instructions can be executed when "C" is + * enabled, and that a write to MISA which would increase IALIGN and cause + * the next instruction to be unaligned is ignored. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define RVC (1 << ('C'-'A')) +#define RVV (1 << ('V'-'A')) + +.option norvc + .text + .global _start +_start: + lla t0, trap + csrw mtvec, t0 + + csrr t0, misa + li t1, RVC + not t1, t1 + and t0, t0, t1 + csrw misa, t0 + csrr t1, misa + li a0, 2 # fail code + bne t0, t1, _exit # Could not clear RVC in MISA + + li t1, RVC + or t0, t0, t1 + csrw misa, t0 + csrr t1, misa + li a0, 3 # fail code + bne t0, t1, _exit # Could not set RVC in MISA + + j unalign +. = . + 2 +unalign: + + li t1, RVC + not t1, t1 + and t0, t0, t1 + csrw misa, t0 + csrr t1, misa + li a0, 4 # fail code + beq t0, t1, _exit # Was able to clear RVC in MISA + + li t0, (RVC|RVV) + not t0, t0 + and t0, t0, t1 + csrw misa, t0 + csrr t0, misa + li a0, 5 # fail code + bne t0, t1, _exit # MISA write was not ignored (RVV was cleared) + + j realign +. = . + 2 +realign: + + # Success! + li a0, 0 + j _exit + +trap: + # Any trap is a fail code 1 + li a0, 1 + +# Exit code in a0 +_exit: + lla a1, semiargs + li t0, 0x20026 # ADP_Stopped_ApplicationExit + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 # TARGET_SYS_EXIT_EXTENDED + + # Semihosting call sequence + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 From 598ef9c81a7f3a9edefdb71e87ca51fb9a4b28bd Mon Sep 17 00:00:00 2001 From: Xu Liu Date: Mon, 17 Aug 2026 16:30:05 +0800 Subject: [PATCH 810/860] target/riscv: Make Zcmt JVT loads endian-aware The Zcmt specification says JVT table entries follow the current data endianness. Support that behavior as described by the specification so big-endian guests can use JVT tables stored in big-endian form. Signed-off-by: Xu Liu Reviewed-by: Alistair Francis Message-ID: <02050B1BBB8815BE+20260817083005.2392-1-liuxu@nucleisys.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/zce_helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/tcg/zce_helper.c b/target/riscv/tcg/zce_helper.c index 15bf0a99c8..c6ba4c1a47 100644 --- a/target/riscv/tcg/zce_helper.c +++ b/target/riscv/tcg/zce_helper.c @@ -18,12 +18,14 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "internals.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" target_ulong HELPER(cm_jalt)(CPURISCVState *env, uint32_t index) { unsigned mmu_index = cpu_mmu_index(env_cpu(env), true); + MemOp endian = mo_endian_env(env); MemOpIdx oi; #if !defined(CONFIG_USER_ONLY) @@ -45,11 +47,11 @@ target_ulong HELPER(cm_jalt)(CPURISCVState *env, uint32_t index) } if (xlen == 32) { - oi = make_memop_idx(MO_LEUL, mmu_index); + oi = make_memop_idx(MO_UL | endian, mmu_index); t0 = base + (index << 2); target = cpu_ldl_code_mmu(env, t0, oi, 0); } else { - oi = make_memop_idx(MO_LEUQ, mmu_index); + oi = make_memop_idx(MO_UQ | endian, mmu_index); t0 = base + (index << 3); target = cpu_ldq_code_mmu(env, t0, oi, 0); } From 85d38315fd19cda7fcfec5350f27cdfa15742600 Mon Sep 17 00:00:00 2001 From: "Christian S. Lima" Date: Sat, 8 Aug 2026 03:18:54 +0000 Subject: [PATCH 811/860] target/riscv/tcg: sret in virtual user mode raises virtual instruction exception Currently, when a `sret` is executed in virtual user mode, qemu raise an `illegal instruction exception`, but in this case the correct behavior is to raise a `virtual instruction exception` and the code already contains a check to it, but it's not enough to catch. This patch is useful to improve the correctness of the virtualization of the risc v architecture. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3622 Signed-off-by: Christian S. Lima Reviewed-by: Alistair Francis Message-ID: <20260808031849.59726-1-christianslima@proton.me> Signed-off-by: Alistair Francis --- target/riscv/tcg/op_helper.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/riscv/tcg/op_helper.c b/target/riscv/tcg/op_helper.c index a7bd42d2c6..3e94005d2b 100644 --- a/target/riscv/tcg/op_helper.c +++ b/target/riscv/tcg/op_helper.c @@ -308,6 +308,11 @@ target_ulong helper_sret(CPURISCVState *env) const privilege_mode_t src_priv = env->priv; const bool src_virt = env->virt_enabled; + if ((env->virt_enabled && env->priv < PRV_S) || + (env->virt_enabled && get_field(env->hstatus, HSTATUS_VTSR))) { + riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); + } + if (!(env->priv >= PRV_S)) { riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } @@ -323,10 +328,6 @@ target_ulong helper_sret(CPURISCVState *env) riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - if (env->virt_enabled && get_field(env->hstatus, HSTATUS_VTSR)) { - riscv_raise_exception(env, RISCV_EXCP_VIRT_INSTRUCTION_FAULT, GETPC()); - } - mstatus = env->mstatus; prev_priv = get_field(mstatus, MSTATUS_SPP); mstatus = set_field(mstatus, MSTATUS_SIE, From 42feb94b790523f83c1d39141c59face0e327211 Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Fri, 7 Aug 2026 19:36:16 +0900 Subject: [PATCH 812/860] hw/ufs: Separate the controller core from the PCI frontend UfsHc is currently also the PCI device instance, tying common code to PCI-specific DMA and IRQ interfaces and preventing reuse by non-PCI frontends. Make UfsHc transport-independent and embed it in UfsPciState. Move the PCI-specific handling to ufs-pci.c, pass the owning DeviceState and DMA AddressSpace to the core, and record the core explicitly in UfsBus. Split the common implementation into CONFIG_UFS, selected by CONFIG_UFS_PCI. The user-visible "ufs" device and its properties remain unchanged. No functional change is intended. Signed-off-by: Jeuk Kim --- hw/ufs/Kconfig | 5 ++ hw/ufs/lu.c | 2 +- hw/ufs/meson.build | 3 +- hw/ufs/trace-events | 4 +- hw/ufs/ufs-pci.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ hw/ufs/ufs.c | 109 +++++++++--------------------------------- hw/ufs/ufs.h | 14 ++++-- 7 files changed, 153 insertions(+), 96 deletions(-) create mode 100644 hw/ufs/ufs-pci.c diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig index b7b3392e85..47e28a30ea 100644 --- a/hw/ufs/Kconfig +++ b/hw/ufs/Kconfig @@ -1,4 +1,9 @@ +config UFS + bool + select SCSI + config UFS_PCI bool default y if PCI_DEVICES depends on PCI + select UFS diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 13f4a90145..eeca865eb5 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -497,7 +497,7 @@ static void ufs_lu_realize(DeviceState *dev, Error **errp) { UfsLu *lu = DO_UPCAST(UfsLu, qdev, dev); BusState *s = qdev_get_parent_bus(dev); - UfsHc *u = UFS(s->parent); + UfsHc *u = UFS_BUS(s)->hc; BlockBackend *blk = lu->conf.blk; if (!ufs_lu_check_constraints(lu, errp)) { diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build index 6e68328b93..880fc52c05 100644 --- a/hw/ufs/meson.build +++ b/hw/ufs/meson.build @@ -1 +1,2 @@ -system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs.c', 'lu.c')) +system_ss.add(when: 'CONFIG_UFS', if_true: files('ufs.c', 'lu.c')) +system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs-pci.c')) diff --git a/hw/ufs/trace-events b/hw/ufs/trace-events index 662d9afee3..0cd3ba9b02 100644 --- a/hw/ufs/trace-events +++ b/hw/ufs/trace-events @@ -1,6 +1,6 @@ # ufs.c -ufs_irq_raise(void) "INTx" -ufs_irq_lower(void) "INTx" +ufs_irq_raise(void) "IRQ" +ufs_irq_lower(void) "IRQ" ufs_mmio_read(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" ufs_mmio_write(uint64_t addr, uint64_t data, unsigned size) "addr 0x%"PRIx64" data 0x%"PRIx64" size %d" ufs_process_db(uint32_t slot) "UTRLDBR slot %"PRIu32"" diff --git a/hw/ufs/ufs-pci.c b/hw/ufs/ufs-pci.c new file mode 100644 index 0000000000..8abd7d98e3 --- /dev/null +++ b/hw/ufs/ufs-pci.c @@ -0,0 +1,112 @@ +/* + * QEMU Universal Flash Storage (UFS) PCI Controller + * + * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved. + * + * Written by Jeuk Kim + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/** + * Usage + * ----- + * + * Add options: + * -drive file=,if=none,id= + * -device ufs,serial=,id=, \ + * nutrs=,nutmrs= + * -device ufs-lu,drive=,bus= + */ + +#include "qemu/osdep.h" +#include "hw/core/irq.h" +#include "hw/core/qdev-properties.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_device.h" +#include "migration/vmstate.h" +#include "ufs.h" + +#define TYPE_UFS_PCI "ufs" +OBJECT_DECLARE_SIMPLE_TYPE(UfsPciState, UFS_PCI) + +struct UfsPciState { + PCIDevice parent_obj; + UfsHc ufs; +}; + +static void ufs_pci_realize(PCIDevice *pci_dev, Error **errp) +{ + UfsPciState *s = UFS_PCI(pci_dev); + UfsHc *u = &s->ufs; + uint8_t *pci_conf = pci_dev->config; + + pci_conf[PCI_INTERRUPT_PIN] = 1; + pci_config_set_prog_interface(pci_conf, 0x1); + u->irq = pci_allocate_irq(pci_dev); + if (!ufs_realize(u, DEVICE(pci_dev), pci_get_address_space(pci_dev), + errp)) { + qemu_free_irq(u->irq); + u->irq = NULL; + return; + } + + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem); +} + +static void ufs_pci_exit(PCIDevice *pci_dev) +{ + UfsPciState *s = UFS_PCI(pci_dev); + + ufs_unrealize(&s->ufs); + qemu_free_irq(s->ufs.irq); +} + +static const Property ufs_pci_props[] = { + DEFINE_PROP_STRING("serial", UfsPciState, ufs.params.serial), + DEFINE_PROP_UINT8("nutrs", UfsPciState, ufs.params.nutrs, 32), + DEFINE_PROP_UINT8("nutmrs", UfsPciState, ufs.params.nutmrs, 8), + DEFINE_PROP_BOOL("mcq", UfsPciState, ufs.params.mcq, false), + DEFINE_PROP_UINT8("mcq-maxq", UfsPciState, ufs.params.mcq_maxq, 2), + DEFINE_PROP_UINT32("wb-max-size", UfsPciState, ufs.params.wb_max_size, + 0x400), + DEFINE_PROP_UINT32("wb-min-size", UfsPciState, ufs.params.wb_min_size, + 0x100), +}; + +static const VMStateDescription ufs_pci_vmstate = { + .name = "ufs", + .unmigratable = 1, +}; + +static void ufs_pci_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); + + pc->realize = ufs_pci_realize; + pc->exit = ufs_pci_exit; + pc->vendor_id = PCI_VENDOR_ID_REDHAT; + pc->device_id = PCI_DEVICE_ID_REDHAT_UFS; + pc->class_id = PCI_CLASS_STORAGE_UFS; + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->desc = "Universal Flash Storage"; + device_class_set_props(dc, ufs_pci_props); + dc->vmsd = &ufs_pci_vmstate; +} + +static const TypeInfo ufs_pci_info = { + .name = TYPE_UFS_PCI, + .parent = TYPE_PCI_DEVICE, + .class_init = ufs_pci_class_init, + .instance_size = sizeof(UfsPciState), + .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, +}; + +static void ufs_pci_register_types(void) +{ + type_register_static(&ufs_pci_info); +} + +type_init(ufs_pci_register_types) diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 464fd465b3..160a4ac4f3 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -11,19 +11,10 @@ /** * Reference Specs: https://www.jedec.org/, 4.1 * - * Usage - * ----- - * - * Add options: - * -drive file=,if=none,id= - * -device ufs,serial=,id=, \ - * nutrs=,nutmrs= - * -device ufs-lu,drive=,bus= */ #include "qemu/osdep.h" #include "qapi/error.h" -#include "migration/vmstate.h" #include "scsi/constants.h" #include "hw/core/irq.h" #include "trace.h" @@ -102,7 +93,7 @@ static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size) return MEMTX_DECODE_ERROR; } - return pci_dma_read(PCI_DEVICE(u), addr, buf, size); + return dma_memory_read(u->dma_as, addr, buf, size, MEMTXATTRS_UNSPECIFIED); } static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, @@ -117,7 +108,7 @@ static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf, return MEMTX_DECODE_ERROR; } - return pci_dma_write(PCI_DEVICE(u), addr, buf, size); + return dma_memory_write(u->dma_as, addr, buf, size, MEMTXATTRS_UNSPECIFIED); } static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot) @@ -222,7 +213,7 @@ static MemTxResult ufs_dma_read_prdt(UfsRequest *req) } req->sg = g_malloc0(sizeof(QEMUSGList)); - pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len); + qemu_sglist_init(req->sg, u->dev, prdt_len, u->dma_as); req->data_len = 0; for (uint16_t i = 0; i < prdt_len; ++i) { @@ -317,14 +308,12 @@ static MemTxResult ufs_dma_write_upiu(UfsRequest *req) static void ufs_irq_check(UfsHc *u) { - PCIDevice *pci = PCI_DEVICE(u); - if ((u->reg.is & UFS_INTR_MASK) & u->reg.ie) { trace_ufs_irq_raise(); - pci_irq_assert(pci); + qemu_irq_raise(u->irq); } else { trace_ufs_irq_lower(); - pci_irq_deassert(pci); + qemu_irq_lower(u->irq); } } @@ -596,7 +585,7 @@ static bool ufs_mcq_create_sq(UfsHc *u, uint8_t qid, uint32_t attr) sq->size = qsize; sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq, - &DEVICE(u)->mem_reentrancy_guard); + &u->dev->mem_reentrancy_guard); sq->req = g_new0(UfsRequest, sq->size); QTAILQ_INIT(&sq->req_list); for (int i = 0; i < sq->size; i++) { @@ -690,7 +679,7 @@ static bool ufs_mcq_create_cq(UfsHc *u, uint8_t qid, uint32_t attr) cq->size = qsize; cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq, - &DEVICE(u)->mem_reentrancy_guard); + &u->dev->mem_reentrancy_guard); QTAILQ_INIT(&cq->req_list); u->cq[qid] = cq; @@ -2488,19 +2477,6 @@ static bool ufs_check_constraints(UfsHc *u, Error **errp) return true; } -static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev) -{ - uint8_t *pci_conf = pci_dev->config; - - pci_conf[PCI_INTERRUPT_PIN] = 1; - pci_config_set_prog_interface(pci_conf, 0x1); - - memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs", - u->reg_size); - pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem); - u->irq = pci_allocate_irq(pci_dev); -} - static void ufs_init_state(UfsHc *u) { u->req_list = g_new0(UfsRequest, u->params.nutrs); @@ -2512,10 +2488,10 @@ static void ufs_init_state(UfsHc *u) u->req_list[i].state = UFS_REQUEST_IDLE; } - u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u, - &DEVICE(u)->mem_reentrancy_guard); - u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u, - &DEVICE(u)->mem_reentrancy_guard); + u->doorbell_bh = + qemu_bh_new_guarded(ufs_process_req, u, &u->dev->mem_reentrancy_guard); + u->complete_bh = + qemu_bh_new_guarded(ufs_sendback_req, u, &u->dev->mem_reentrancy_guard); if (u->params.mcq) { memset(u->sq, 0, sizeof(u->sq)); @@ -2689,35 +2665,35 @@ static void ufs_init_hc(UfsHc *u) timer_mod(&u->idle_timer, now + UFS_IDLE_TIMER_TICK); } -static void ufs_realize(PCIDevice *pci_dev, Error **errp) +bool ufs_realize(UfsHc *u, DeviceState *dev, AddressSpace *dma_as, Error **errp) { - UfsHc *u = UFS(pci_dev); + u->dev = dev; + u->dma_as = dma_as; if (!ufs_check_constraints(u, errp)) { - return; + return false; } - qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev, - u->parent_obj.qdev.id); + qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, dev, dev->id); + u->bus.hc = u; ufs_init_state(u); ufs_init_hc(u); - ufs_init_pci(u, pci_dev); + memory_region_init_io(&u->iomem, OBJECT(dev), &ufs_mmio_ops, u, "ufs", + u->reg_size); ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN); ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN); ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN); ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN); + + return true; } -static void ufs_exit(PCIDevice *pci_dev) +void ufs_unrealize(UfsHc *u) { - UfsHc *u = UFS(pci_dev); - timer_del(&u->idle_timer); - qemu_free_irq(u->irq); - qemu_bh_delete(u->doorbell_bh); qemu_bh_delete(u->complete_bh); @@ -2740,38 +2716,6 @@ static void ufs_exit(PCIDevice *pci_dev) } } -static const Property ufs_props[] = { - DEFINE_PROP_STRING("serial", UfsHc, params.serial), - DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32), - DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8), - DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false), - DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2), - DEFINE_PROP_UINT32("wb-max-size", UfsHc, params.wb_max_size, 0x400), - DEFINE_PROP_UINT32("wb-min-size", UfsHc, params.wb_min_size, 0x100), -}; - -static const VMStateDescription ufs_vmstate = { - .name = "ufs", - .unmigratable = 1, -}; - -static void ufs_class_init(ObjectClass *oc, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); - - pc->realize = ufs_realize; - pc->exit = ufs_exit; - pc->vendor_id = PCI_VENDOR_ID_REDHAT; - pc->device_id = PCI_DEVICE_ID_REDHAT_UFS; - pc->class_id = PCI_CLASS_STORAGE_UFS; - - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->desc = "Universal Flash Storage"; - device_class_set_props(dc, ufs_props); - dc->vmsd = &ufs_vmstate; -} - static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev, Error **errp) { @@ -2798,14 +2742,6 @@ static void ufs_bus_class_init(ObjectClass *class, const void *data) bc->check_address = ufs_bus_check_address; } -static const TypeInfo ufs_info = { - .name = TYPE_UFS, - .parent = TYPE_PCI_DEVICE, - .class_init = ufs_class_init, - .instance_size = sizeof(UfsHc), - .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, -}; - static const TypeInfo ufs_bus_info = { .name = TYPE_UFS_BUS, .parent = TYPE_BUS, @@ -2816,7 +2752,6 @@ static const TypeInfo ufs_bus_info = { static void ufs_register_types(void) { - type_register_static(&ufs_info); type_register_static(&ufs_bus_info); } diff --git a/hw/ufs/ufs.h b/hw/ufs/ufs.h index feb47f460d..aa8361d93d 100644 --- a/hw/ufs/ufs.h +++ b/hw/ufs/ufs.h @@ -11,9 +11,11 @@ #ifndef HW_UFS_UFS_H #define HW_UFS_UFS_H -#include "hw/pci/pci_device.h" +#include "hw/core/qdev.h" #include "hw/scsi/scsi.h" #include "block/ufs.h" +#include "scsi/constants.h" +#include "system/dma.h" #define UFS_MAX_LUS 32 #define UFS_MAX_MCQ_QNUM 32 @@ -27,6 +29,7 @@ typedef struct UfsBusClass { typedef struct UfsBus { BusState parent_bus; + struct UfsHc *hc; } UfsBus; #define TYPE_UFS_BUS "ufs-bus" @@ -141,7 +144,8 @@ typedef struct UfsWb { } UfsWb; typedef struct UfsHc { - PCIDevice parent_obj; + DeviceState *dev; + AddressSpace *dma_as; UfsBus bus; MemoryRegion iomem; UfsReg reg; @@ -268,9 +272,6 @@ static inline bool ufs_is_write_req(UfsRequest *req) return (cmd == WRITE_6) || (cmd == WRITE_10) || (cmd == WRITE_16); } -#define TYPE_UFS "ufs" -#define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS) - #define TYPE_UFS_LU "ufs-lu" #define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU) @@ -302,4 +303,7 @@ void ufs_build_query_response(UfsRequest *req); void ufs_complete_req(UfsRequest *req, UfsReqResult req_result); void ufs_wb_update_avail_buffer(UfsHc *u); void ufs_init_wlu(UfsLu *wlu, uint8_t wlun); +bool ufs_realize(UfsHc *u, DeviceState *dev, AddressSpace *dma_as, + Error **errp); +void ufs_unrealize(UfsHc *u); #endif /* HW_UFS_UFS_H */ From 6ed05d5e65208a5ee23a02667d5df0dc6491c156 Mon Sep 17 00:00:00 2001 From: Jeuk Kim Date: Fri, 7 Aug 2026 19:36:17 +0900 Subject: [PATCH 813/860] hw/ufs: Add a generic SysBus frontend Add TYPE_SYSBUS_UFS as a reusable frontend for the transport-independent UFS controller core. Use the system memory address space for DMA, expose the controller MMIO region and IRQ through SysBus, and provide the same properties and migration policy as the PCI frontend. Platform-specific controllers can derive from this type and keep only their hardware-specific behavior. Signed-off-by: Jeuk Kim --- hw/ufs/Kconfig | 4 +++ hw/ufs/meson.build | 1 + hw/ufs/ufs-sysbus.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ hw/ufs/ufs-sysbus.h | 22 +++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 hw/ufs/ufs-sysbus.c create mode 100644 hw/ufs/ufs-sysbus.h diff --git a/hw/ufs/Kconfig b/hw/ufs/Kconfig index 47e28a30ea..0e40f08a41 100644 --- a/hw/ufs/Kconfig +++ b/hw/ufs/Kconfig @@ -7,3 +7,7 @@ config UFS_PCI default y if PCI_DEVICES depends on PCI select UFS + +config UFS_SYSBUS + bool + select UFS diff --git a/hw/ufs/meson.build b/hw/ufs/meson.build index 880fc52c05..a84627b96a 100644 --- a/hw/ufs/meson.build +++ b/hw/ufs/meson.build @@ -1,2 +1,3 @@ system_ss.add(when: 'CONFIG_UFS', if_true: files('ufs.c', 'lu.c')) system_ss.add(when: 'CONFIG_UFS_PCI', if_true: files('ufs-pci.c')) +system_ss.add(when: 'CONFIG_UFS_SYSBUS', if_true: files('ufs-sysbus.c')) diff --git a/hw/ufs/ufs-sysbus.c b/hw/ufs/ufs-sysbus.c new file mode 100644 index 0000000000..84de2e95ac --- /dev/null +++ b/hw/ufs/ufs-sysbus.c @@ -0,0 +1,80 @@ +/* + * QEMU Universal Flash Storage (UFS) sysbus controller + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/core/qdev-properties.h" +#include "ufs-sysbus.h" +#include "migration/vmstate.h" +#include "system/address-spaces.h" + +static void ufs_sysbus_realize(DeviceState *dev, Error **errp) +{ + SysbusUfsState *s = SYSBUS_UFS(dev); + + if (!ufs_realize(&s->ufs, dev, &address_space_memory, errp)) { + return; + } + + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->ufs.iomem); +} + +static void ufs_sysbus_unrealize(DeviceState *dev) +{ + SysbusUfsState *s = SYSBUS_UFS(dev); + + ufs_unrealize(&s->ufs); +} + +static void ufs_sysbus_init(Object *obj) +{ + SysbusUfsState *s = SYSBUS_UFS(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->ufs.irq); +} + +static const Property ufs_sysbus_props[] = { + DEFINE_PROP_STRING("serial", SysbusUfsState, ufs.params.serial), + DEFINE_PROP_UINT8("nutrs", SysbusUfsState, ufs.params.nutrs, 32), + DEFINE_PROP_UINT8("nutmrs", SysbusUfsState, ufs.params.nutmrs, 8), + DEFINE_PROP_BOOL("mcq", SysbusUfsState, ufs.params.mcq, false), + DEFINE_PROP_UINT8("mcq-maxq", SysbusUfsState, ufs.params.mcq_maxq, 2), + DEFINE_PROP_UINT32("wb-max-size", SysbusUfsState, ufs.params.wb_max_size, + 0x400), + DEFINE_PROP_UINT32("wb-min-size", SysbusUfsState, ufs.params.wb_min_size, + 0x100), +}; + +static const VMStateDescription ufs_sysbus_vmstate = { + .name = TYPE_SYSBUS_UFS, + .unmigratable = 1, +}; + +static void ufs_sysbus_class_init(ObjectClass *oc, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = ufs_sysbus_realize; + dc->unrealize = ufs_sysbus_unrealize; + dc->vmsd = &ufs_sysbus_vmstate; + dc->desc = "Universal Flash Storage"; + device_class_set_props(dc, ufs_sysbus_props); + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo ufs_sysbus_info = { + .name = TYPE_SYSBUS_UFS, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysbusUfsState), + .instance_init = ufs_sysbus_init, + .class_init = ufs_sysbus_class_init, +}; + +static void ufs_sysbus_register_types(void) +{ + type_register_static(&ufs_sysbus_info); +} + +type_init(ufs_sysbus_register_types) diff --git a/hw/ufs/ufs-sysbus.h b/hw/ufs/ufs-sysbus.h new file mode 100644 index 0000000000..71b6787d3a --- /dev/null +++ b/hw/ufs/ufs-sysbus.h @@ -0,0 +1,22 @@ +/* + * QEMU Universal Flash Storage (UFS) sysbus controller + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_UFS_UFS_SYSBUS_H +#define HW_UFS_UFS_SYSBUS_H + +#include "hw/core/sysbus.h" +#include "ufs.h" + +#define TYPE_SYSBUS_UFS "sysbus-ufs" +OBJECT_DECLARE_SIMPLE_TYPE(SysbusUfsState, SYSBUS_UFS) + +struct SysbusUfsState { + SysBusDevice parent_obj; + + UfsHc ufs; +}; + +#endif /* HW_UFS_UFS_SYSBUS_H */ From 8fda982a7c083251635a2ce41dcc068ed0e5c124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 25 Aug 2026 11:41:14 +0400 Subject: [PATCH 814/860] hw/misc: fix trace-events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit 8041d1730871 accidentally removed the vmlaunchupdate.c trace events. Fixes: 8041d1730871 ("tests/qtest: add test for K230 gsdma") Reviewed-by: Daniel P. Berrangé Reviewed-by: Luigi Leonardi Signed-off-by: Marc-André Lureau Message-ID: <20260825074114.853069-1-marcandre.lureau@redhat.com> --- hw/misc/trace-events | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 16db4ba0cc..0b8be3d0f2 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -446,3 +446,9 @@ iommu_testdev_dma_armed(bool armed) "armed=%d" # k230_decomp_gzip.c k230_decomp_gzip_read(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP read: [0x%"PRIx64"] size %u -> 0x%"PRIx64 k230_decomp_gzip_write(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP write: [0x%"PRIx64"] size %u <- 0x%"PRIx64 + +# vmlaunchupdate.c +launch_update_write(void) "" +vmlaunch_reset_enter(void) "" +vm_launchupdate_finalize(void) "" +restore_host_x86_igvm(void) "" From b5102872b8de250c58442b8d83c91566d5cca2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 27 Jul 2026 00:44:10 +0400 Subject: [PATCH 815/860] migration/multifd: fix Error leak in multifd_recv_terminate_threads() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If err != NULL, free it. Fixes: 11dd7be57524 ("migration/multifd: Remove p->quit from recv side") Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Marc-André Lureau Message-ID: <20260727-fix2-v2-11-d0c4831ed7ea@redhat.com> --- migration/multifd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/multifd.c b/migration/multifd.c index dbad525d2a..503014f76b 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1056,6 +1056,7 @@ static void multifd_recv_terminate_threads(Error *err) trace_multifd_recv_terminate_threads(err != NULL); if (qatomic_xchg(&multifd_recv_state->exiting, 1)) { + error_free(err); return; } From df42a1589fad9bd140df4e48aabc32b02bea5b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 9 Jul 2026 15:12:49 +0400 Subject: [PATCH 816/860] hw/core/machine: fix fdt memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MachineState fdt field is allocated by various machine types via create_device_tree(), load_device_tree(), or similar, but was never freed in machine_finalize(). Add the missing g_free() call. Reviewed-by: Zhao Liu Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Marc-André Lureau Message-ID: <20260709111249.1107640-1-marcandre.lureau@redhat.com> --- hw/core/machine.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/core/machine.c b/hw/core/machine.c index e617f7804d..8939ae1666 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1314,6 +1314,7 @@ static void machine_finalize(Object *obj) g_free(ms->nvdimms_state); g_free(ms->numa_state); g_free(ms->audiodev); + g_free(ms->fdt); } bool machine_usb(MachineState *machine) From ab7183ed4eecb4727532e3ffe5953d127e102c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 6 Aug 2026 13:40:27 +0400 Subject: [PATCH 817/860] hw/display/qxl: validate primary surface stride against width The existing validation in qxl_create_guest_primary() checks that abs(stride) * height fits in vgamem_size and that stride is 4-byte aligned, but never checks that abs(stride) is large enough to hold one row of pixels for the declared width and format. A malicious guest can create a primary surface with a stride much smaller than width * bytes_per_pixel (e.g. stride=4 for a 64-wide 32bpp surface). The spice server rejects this via red_validate_surface(), but the return is void and QEMU unconditionally proceeds to set up the local rendering state. On the next display refresh, VNC or SDL reads width * bytes_pp per scanline from a region backed by only stride bytes per row, causing a host-side out-of-bounds read. Add three checks in qxl_create_guest_primary() before creating the surface: - reject unknown surface formats - reject zero width or height - reject surfaces where abs(stride) < width * bytes_per_pixel Also fix three related issues in qxl-render.c: - qxl_blit() used abs_stride to advance the dst pointer into the DisplaySurface, but when stride is negative the DisplaySurface is a packed buffer whose stride may be smaller. Use surface_stride() instead. - qxl_render_update_area_unlocked() uses guest_head0_width (set via QXL_IO_MONITORS_CONFIG_ASYNC) without validating it against abs_stride, bypassing the new validation. Clamp the effective width to abs_stride / bytes_pp to prevent out-of-bounds access while tolerating the normal transient where the monitor config arrives before the primary surface is resized to match. - Similarly, guest_head0_height bypasses qxl_create_guest_primary() validation. Without clamping, abs_stride * height can overrun vgamem_size, and the product can also overflow 32 bits (e.g. abs_stride=16 MiB, height=256 wraps to zero), defeating the qxl_phys2virt() bounds check. Clamp height to vgamem_size / abs_stride to prevent both. While touch it, fix some endianness issues. Fixes: CVE-2026-16271 Fixes: a19cbfb34642 ("spice: add qxl device") Fixes: 979f7ef8966b ("qxl: use guest_monitor_config for local renderer.") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3637 Reported-by: huntr bubble Signed-off-by: Marc-Andre Lureau Reviewed-by: Akihiko Odaki Message-ID: <20260806094028.640676-1-marcandre.lureau@redhat.com> --- hw/display/qxl-render.c | 66 +++++++++++++++++++++++++---------------- hw/display/qxl.c | 59 ++++++++++++++++++++++++++++++++++++ hw/display/qxl.h | 2 ++ 3 files changed, 101 insertions(+), 26 deletions(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index 4799c9e8be..348ddba768 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -27,6 +27,7 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) { DisplaySurface *surface = qemu_console_surface(qxl->vga.con); + int dst_stride = surface_stride(surface); uint8_t *dst = surface_data(surface); uint8_t *src; int len, i; @@ -45,14 +46,14 @@ static void qxl_blit(PCIQXLDevice *qxl, QXLRect *rect) } else { src += rect->top * qxl->guest_primary.abs_stride; } - dst += rect->top * qxl->guest_primary.abs_stride; + dst += rect->top * dst_stride; src += rect->left * qxl->guest_primary.bytes_pp; dst += rect->left * qxl->guest_primary.bytes_pp; len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; for (i = rect->top; i < rect->bottom; i++) { memcpy(dst, src, len); - dst += qxl->guest_primary.abs_stride; + dst += dst_stride; src += qxl->guest_primary.qxl_stride; } } @@ -61,30 +62,13 @@ void qxl_render_resize(PCIQXLDevice *qxl) { QXLSurfaceCreate *sc = &qxl->guest_primary.surface; - qxl->guest_primary.qxl_stride = sc->stride; - qxl->guest_primary.abs_stride = abs(sc->stride); + qxl->guest_primary.qxl_stride = le32_to_cpu(sc->stride); + qxl->guest_primary.abs_stride = abs(qxl->guest_primary.qxl_stride); qxl->guest_primary.resized++; - switch (sc->format) { - case SPICE_SURFACE_FMT_16_555: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 15; - break; - case SPICE_SURFACE_FMT_16_565: - qxl->guest_primary.bytes_pp = 2; - qxl->guest_primary.bits_pp = 16; - break; - case SPICE_SURFACE_FMT_32_xRGB: - case SPICE_SURFACE_FMT_32_ARGB: - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - default: - fprintf(stderr, "%s: unhandled format: %x\n", __func__, - qxl->guest_primary.surface.format); - qxl->guest_primary.bytes_pp = 4; - qxl->guest_primary.bits_pp = 32; - break; - } + /* fallback to default bpp if format is unknown */ + qxl_format_bpp(qxl, le32_to_cpu(sc->format), + &qxl->guest_primary.bytes_pp, + &qxl->guest_primary.bits_pp); } static void qxl_set_rect_to_surface(PCIQXLDevice *qxl, QXLRect *area) @@ -101,15 +85,45 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) DisplaySurface *surface; int width = qxl->guest_head0_width ?: qxl->guest_primary.surface.width; int height = qxl->guest_head0_height ?: qxl->guest_primary.surface.height; + uint64_t map_height; int i; + if (width <= 0 || height <= 0) { + goto end; + } + + if (qxl->guest_primary.bytes_pp > 0) { + int max_width = qxl->guest_primary.abs_stride + / qxl->guest_primary.bytes_pp; + width = MIN(width, max_width); + } + + if (qxl->guest_primary.qxl_stride < 0) { + /* qxl_blit() uses the primary height to find the first scanline. */ + height = MIN(height, (int)qxl->guest_primary.surface.height); + } + + if (qxl->guest_primary.abs_stride > 0) { + int max_height = qxl->vgamem_size / qxl->guest_primary.abs_stride; + height = MIN(height, max_height); + } + + /* + * height limits the visible update, while map_height is the guest memory + * span validated by qxl_phys2virt(). With a negative stride qxl_blit() + * addresses scanlines from the declared primary height, so a shorter + * monitor still requires validating the full primary surface. + */ + map_height = qxl->guest_primary.qxl_stride < 0 ? + qxl->guest_primary.surface.height : height; + if (qxl->guest_primary.resized) { qxl->guest_primary.resized = 0; qxl->guest_primary.data = qxl_phys2virt(qxl, qxl->guest_primary.surface.mem, MEMSLOT_GROUP_GUEST, qxl->guest_primary.abs_stride - * height); + * map_height); if (!qxl->guest_primary.data) { goto end; } diff --git a/hw/display/qxl.c b/hw/display/qxl.c index b7d871b9ee..384b8767b8 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1489,6 +1489,47 @@ static void qxl_create_guest_primary_complete(PCIQXLDevice *qxl) qxl_render_resize(qxl); } +/* + * Convert a SpiceSurfaceFormat to bytes per pixel and bits per pixel. + * + * Only valid for surface suitable for rendering. + */ +bool qxl_format_bpp(PCIQXLDevice *qxl, SpiceSurfaceFmt format, + uint32_t *bytes_pp, uint32_t *bits_pp) +{ + uint32_t bypp = 4; + uint32_t bipp = 32; + bool ret = true; + + switch (format) { + case SPICE_SURFACE_FMT_16_555: + bypp = 2; + bipp = 15; + break; + case SPICE_SURFACE_FMT_16_565: + bypp = 2; + bipp = 16; + break; + case SPICE_SURFACE_FMT_32_xRGB: + case SPICE_SURFACE_FMT_32_ARGB: + bypp = 4; + bipp = 32; + break; + default: + ret = false; + qxl_set_guest_bug(qxl, "%s: unhandled format: %x", __func__, format); + } + + if (bytes_pp != NULL) { + *bytes_pp = bypp; + } + if (bits_pp != NULL) { + *bits_pp = bipp; + } + + return ret; +} + static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, qxl_async_io async) { @@ -1496,6 +1537,7 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, QXLSurfaceCreate *sc = &qxl->guest_primary.surface; uint32_t requested_height = le32_to_cpu(sc->height); int requested_stride = le32_to_cpu(sc->stride); + uint32_t bytes_pp; if (requested_stride == INT32_MIN || abs(requested_stride) * (uint64_t)requested_height @@ -1532,6 +1574,23 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm, return; } + if (!qxl_format_bpp(qxl, surface.format, &bytes_pp, NULL)) { + return; + } + + if (surface.width == 0 || surface.height == 0) { + qxl_set_guest_bug(qxl, "%s: zero dimension %ux%u", + __func__, surface.width, surface.height); + return; + } + + if ((uint64_t)surface.width * bytes_pp > abs(surface.stride)) { + qxl_set_guest_bug(qxl, "%s: stride too small for width:" + " stride %d width %u bpp %u", + __func__, surface.stride, surface.width, bytes_pp); + return; + } + surface.mouse_mode = true; surface.group_id = MEMSLOT_GROUP_GUEST; if (loadvm) { diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 48d664f777..6f5b96fe86 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -181,6 +181,8 @@ void qxl_spice_oom(PCIQXLDevice *qxl); void qxl_spice_reset_memslots(PCIQXLDevice *qxl); void qxl_spice_reset_image_cache(PCIQXLDevice *qxl); void qxl_spice_reset_cursor(PCIQXLDevice *qxl); +bool qxl_format_bpp(PCIQXLDevice *qxl, SpiceSurfaceFmt format, + uint32_t *bytes_pp, uint32_t *bits_pp); /* qxl-logger.c */ int qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id); From eb2b3a1fa807f47515a1d86e434c60c981310bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 5 Aug 2026 17:01:41 +0400 Subject: [PATCH 818/860] virtio-gpu: use g_try_malloc to avoid guest-triggered abort MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use g_try_malloc/g_try_new0 for guest-controlled allocation, so failure returns an error to the guest rather than crashing the host (glib behaviour). Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3898 Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Message-ID: <20260805130141.211398-1-marcandre.lureau@redhat.com> --- contrib/vhost-user-gpu/vhost-user-gpu.c | 25 ++++++++++----- contrib/vhost-user-gpu/virgl.c | 6 +++- contrib/vhost-user-gpu/vugbm.c | 5 ++- hw/display/virtio-gpu-rutabaga.c | 14 +++++++-- hw/display/virtio-gpu-udmabuf.c | 7 +++-- hw/display/virtio-gpu-virgl.c | 6 +++- hw/display/virtio-gpu.c | 42 ++++++++++++++++++------- 7 files changed, 79 insertions(+), 26 deletions(-) diff --git a/contrib/vhost-user-gpu/vhost-user-gpu.c b/contrib/vhost-user-gpu/vhost-user-gpu.c index 7864881509..933bdbb671 100644 --- a/contrib/vhost-user-gpu/vhost-user-gpu.c +++ b/contrib/vhost-user-gpu/vhost-user-gpu.c @@ -487,7 +487,7 @@ vg_create_mapping_iov(VuGpu *g, struct virtio_gpu_ctrl_command *cmd, struct iovec **iov) { - struct virtio_gpu_mem_entry *ents; + g_autofree struct virtio_gpu_mem_entry *ents = NULL; size_t esize, s; int i; @@ -498,17 +498,22 @@ vg_create_mapping_iov(VuGpu *g, } esize = sizeof(*ents) * ab->nr_entries; - ents = g_malloc(esize); + ents = g_try_malloc(esize); + if (!ents && esize) { + return -1; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(*ab), ents, esize); if (s != esize) { g_critical("%s: command data size incorrect %zu vs %zu", __func__, s, esize); - g_free(ents); return -1; } - *iov = g_new0(struct iovec, ab->nr_entries); + *iov = g_try_new0(struct iovec, ab->nr_entries); + if (!*iov && ab->nr_entries) { + return -1; + } for (i = 0; i < ab->nr_entries; i++) { uint64_t len = ents[i].length; (*iov)[i].iov_len = ents[i].length; @@ -517,12 +522,10 @@ vg_create_mapping_iov(VuGpu *g, g_critical("%s: resource %d element %d", __func__, ab->resource_id, i); g_free(*iov); - g_free(ents); *iov = NULL; return -1; } } - g_free(ents); return 0; } @@ -828,8 +831,14 @@ vg_resource_flush(VuGpu *g, PIXMAN_FORMAT_BPP(pixman_image_get_format(res->image)) / 8; size_t size = width * height * bpp; - void *p = g_malloc(VHOST_USER_GPU_HDR_SIZE + - sizeof(VhostUserGpuUpdate) + size); + void *p = g_try_malloc(VHOST_USER_GPU_HDR_SIZE + + sizeof(VhostUserGpuUpdate) + size); + if (!p) { + pixman_region_fini(®ion); + pixman_region_fini(&finalregion); + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + break; + } VhostUserGpuMsg *msg = p; msg->request = VHOST_USER_GPU_UPDATE; msg->size = sizeof(VhostUserGpuUpdate) + size; diff --git a/contrib/vhost-user-gpu/virgl.c b/contrib/vhost-user-gpu/virgl.c index 550fd03bf5..20bae57d0f 100644 --- a/contrib/vhost-user-gpu/virgl.c +++ b/contrib/vhost-user-gpu/virgl.c @@ -209,7 +209,11 @@ virgl_cmd_submit_3d(VuGpu *g, return; } - buf = g_malloc(cs.size); + buf = g_try_malloc(cs.size); + if (!buf && cs.size) { + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); if (s != cs.size) { diff --git a/contrib/vhost-user-gpu/vugbm.c b/contrib/vhost-user-gpu/vugbm.c index 710d545297..e2d8385fd8 100644 --- a/contrib/vhost-user-gpu/vugbm.c +++ b/contrib/vhost-user-gpu/vugbm.c @@ -13,7 +13,10 @@ static bool mem_alloc_bo(struct vugbm_buffer *buf) { - buf->mmap = g_malloc((uint64_t)buf->width * buf->height * 4); + buf->mmap = g_try_malloc((uint64_t)buf->width * buf->height * 4); + if (!buf->mmap && buf->width && buf->height) { + return false; + } buf->stride = buf->width * 4; return true; } diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index a054f8117f..041216a10d 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -366,10 +366,20 @@ rutabaga_cmd_submit_3d(VirtIOGPU *g, return; } - buf = g_new0(uint8_t, cs.size); + buf = g_try_new0(uint8_t, cs.size); + if (!buf && cs.size) { + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); - CHECK(s == cs.size, cmd); + if (s != cs.size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: size mismatch (%zu/%u)\n", + __func__, s, cs.size); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } rutabaga_cmd.ctx_id = cs.hdr.ctx_id; rutabaga_cmd.cmd = buf; diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 5f08c855dd..816f52a514 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -39,8 +39,11 @@ static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res) return; } - list = g_malloc0(sizeof(struct udmabuf_create_list) + - sizeof(struct udmabuf_create_item) * res->iov_cnt); + list = g_try_malloc0(sizeof(struct udmabuf_create_list) + + sizeof(struct udmabuf_create_item) * res->iov_cnt); + if (!list) { + return; + } for (i = 0; i < res->iov_cnt; i++) { rcu_read_lock(); diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index 6e298f997d..9bda572426 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -620,7 +620,11 @@ static void virgl_cmd_submit_3d(VirtIOGPU *g, return; } - buf = g_malloc(cs.size); + buf = g_try_malloc(cs.size); + if (!buf && cs.size) { + cmd->error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY; + return; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, sizeof(cs), buf, cs.size); if (s != cs.size) { diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index fbb6fec7a0..9eb010082d 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -892,7 +892,10 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, } esize = sizeof(*ents) * nr_entries; - ents = g_malloc(esize); + ents = g_try_malloc(esize); + if (!ents && esize) { + return -1; + } s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num, offset, ents, esize); if (s != esize) { @@ -913,6 +916,7 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, hwaddr len; void *map; + /* TODO: a common DMA map SG helper */ do { len = l; map = dma_memory_map(VIRTIO_DEVICE(g)->dma_as, a, &len, @@ -921,20 +925,27 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, if (!map) { qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for" " element %d\n", __func__, e); - virtio_gpu_cleanup_mapping_iov(g, *iov, v); - g_free(ents); - *iov = NULL; - if (addr) { - g_free(*addr); - *addr = NULL; - } - return -1; + goto err; } if (!(v % 16)) { - *iov = g_renew(struct iovec, *iov, v + 16); + struct iovec *new_iov; + new_iov = g_try_renew(struct iovec, *iov, v + 16); + if (!new_iov) { + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, map, len, + DMA_DIRECTION_TO_DEVICE, len); + goto err; + } + *iov = new_iov; if (addr) { - *addr = g_renew(uint64_t, *addr, v + 16); + uint64_t *new_addr; + new_addr = g_try_renew(uint64_t, *addr, v + 16); + if (!new_addr) { + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, map, len, + DMA_DIRECTION_TO_DEVICE, len); + goto err; + } + *addr = new_addr; } } (*iov)[v].iov_base = map; @@ -952,6 +963,15 @@ int virtio_gpu_create_mapping_iov(VirtIOGPU *g, g_free(ents); return 0; + +err: + virtio_gpu_cleanup_mapping_iov(g, *iov, v); + *iov = NULL; + if (addr) { + g_clear_pointer(addr, g_free); + } + g_free(ents); + return -1; } void virtio_gpu_cleanup_mapping_iov(VirtIOGPU *g, From 0e62034ca10f9d71fba43c8277a6590540217df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sun, 28 Jun 2026 23:50:46 +0400 Subject: [PATCH 819/860] crypto: fix build against nettle >= 4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sha.h has been deprecated. It seems we can rely on sha1.h/sha2.h since we depend on >= 3.7.3. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4184 Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Signed-off-by: Marc-André Lureau --- crypto/hash-nettle.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypto/hash-nettle.c b/crypto/hash-nettle.c index 53f68301ef..2589bbf4c1 100644 --- a/crypto/hash-nettle.c +++ b/crypto/hash-nettle.c @@ -24,7 +24,8 @@ #include "crypto/hash.h" #include "hashpriv.h" #include -#include +#include +#include #include #ifdef CONFIG_CRYPTO_SM3 #include From f4b26072289084a0b182c1faa28aa338fe861c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 12 May 2026 10:56:33 +0400 Subject: [PATCH 820/860] tests: tag slow tests with 'slow' suite for easy filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add several RCU and thread-pool unit tests to the slow_tests dict, and tag all slow tests (both qtest and unit) with a 'slow' suite so they can be excluded or selected via meson test --suite/--no-suite. Acked-by: Fabiano Rosas Signed-off-by: Marc-André Lureau Message-ID: <20260512065633.3542562-1-marcandre.lureau@redhat.com> --- tests/qtest/meson.build | 3 ++- tests/unit/meson.build | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 739df71d8d..cbdef5a545 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -510,6 +510,7 @@ foreach dir : target_dirs protocol: 'tap', timeout: slow_qtests.get(test, 60), priority: slow_qtests.get(test, 60), - suite: ['qtest', 'qtest-' + target_base]) + suite: ['qtest', 'qtest-' + target_base] + + (slow_qtests.has_key(test) ? ['slow'] : [])) endforeach endforeach diff --git a/tests/unit/meson.build b/tests/unit/meson.build index dc3fb954c0..3a9866c1f2 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -184,6 +184,12 @@ slow_tests = { 'test-crypto-tlscredsx509': 90, 'test-crypto-tlssession': 90, 'test-replication': 60, + 'rcutorture': 30, + 'test-rcu-list': 30, + 'test-rcu-simpleq': 30, + 'test-rcu-tailq': 30, + 'test-rcu-slist': 30, + 'test-thread-pool': 30, } foreach test_name, extra: tests @@ -205,5 +211,6 @@ foreach test_name, extra: tests protocol: 'tap', timeout: slow_tests.get(test_name, 30), priority: slow_tests.get(test_name, 30), - suite: ['unit']) + suite: ['unit'] + + (slow_tests.has_key(test_name) ? ['slow'] : [])) endforeach From baca25172d8c195b9abbe6f8a3ab5915dba09a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 20 Aug 2026 17:20:14 +0400 Subject: [PATCH 821/860] ui/egl: fix render node cleanup order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASAN detected some memory leaks when terminating. Release thread-bound EGL state first, destroy the context and terminate the display while the GBM device is still alive, then destroy GBM and close the render-node fd. Reviewed-by: Akihiko Odaki Fixes: a3cf9b55bbdc ("ui/egl: implement display and EGL cleanup") Signed-off-by: Marc-André Lureau Message-ID: <20260820132014.2729748-1-marcandre.lureau@redhat.com> --- ui/egl-helpers.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index d689f187c4..e89cc7c49a 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -736,19 +736,22 @@ bool egl_init(const char *rendernode, DisplayGLMode mode, Error **errp) void egl_cleanup(void) { + if (qemu_egl_display) { + eglReleaseThread(); + } + if (qemu_egl_rn_ctx) { eglDestroyContext(qemu_egl_display, qemu_egl_rn_ctx); qemu_egl_rn_ctx = NULL; } + if (qemu_egl_display) { + eglTerminate(qemu_egl_display); + qemu_egl_display = NULL; + } + #ifdef CONFIG_GBM g_clear_pointer(&qemu_egl_rn_gbm_dev, gbm_device_destroy); g_clear_fd(&qemu_egl_rn_fd, NULL); #endif - - if (qemu_egl_display) { - eglReleaseThread(); - eglTerminate(qemu_egl_display); - qemu_egl_display = NULL; - } } From 86fc385ca082b21683b42c7c7c5cd709ce903130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 20 Aug 2026 17:19:33 +0400 Subject: [PATCH 822/860] ui/egl: fix qemu_egl_display type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EGLDisplay is already a pointer type (void *), so declaring qemu_egl_display as EGLDisplay * makes it void **, which doesn't match any of its usages. Fixes: 7ced9e9f6da2 ("ui: add egl-helpers") Reviewed-by: Akihiko Odaki Signed-off-by: Marc-André Lureau Message-ID: <20260820131933.2729240-1-marcandre.lureau@redhat.com> --- include/ui/egl-helpers.h | 2 +- ui/egl-helpers.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 405ddd9125..679c79eacd 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -9,7 +9,7 @@ #include "ui/console.h" #include "ui/shader.h" -extern EGLDisplay *qemu_egl_display; +extern EGLDisplay qemu_egl_display; extern EGLConfig qemu_egl_config; extern DisplayGLMode qemu_egl_mode; extern bool qemu_egl_angle_d3d; diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index e89cc7c49a..8e3729a54d 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -25,7 +25,7 @@ #include "trace.h" #include "standard-headers/drm/drm_fourcc.h" -EGLDisplay *qemu_egl_display; +EGLDisplay qemu_egl_display; EGLConfig qemu_egl_config; DisplayGLMode qemu_egl_mode; bool qemu_egl_angle_d3d; From 4fd6561228c37b6a210242157cf6c1e574b364dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 15 Aug 2026 11:24:21 +0400 Subject: [PATCH 823/860] tests/functional: fix pylint false positives for cv2 module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add generated-members=cv2.* to pylintrc so pylint skips member checking on the cv2 C extension module, whose members are not visible to static analysis. Silence: 2026-08-15 10:42:08,710 - INFO: qemu-test.test_pylint Checking files in /home/elmarco/src/qemu.qom-qapi/tests/functional/arm with pylint 2026-08-15 10:42:10,941 - ERROR: qemu-test.test_pylint "/home/elmarco/src/qemu.qom-qapi/tests/functional/arm/test_integratorcp.py:83: E1101: Module 'cv2' has no 'imread' member (no-member)" Note: I also tried with extension-pkg-allow-list, but that didn't work for some reason. Reviewed-by: Thomas Huth Signed-off-by: Marc-André Lureau Message-ID: <20260815072421.4117291-1-marcandre.lureau@redhat.com> --- tests/functional/pylintrc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/functional/pylintrc b/tests/functional/pylintrc index 949bea611f..373aabd6ca 100644 --- a/tests/functional/pylintrc +++ b/tests/functional/pylintrc @@ -79,6 +79,12 @@ disable=bad-inline-option, useless-suppression, +[TYPECHECK] + +# cv2 is a C extension module whose members are not visible to pylint +generated-members=cv2.* + + [SIMILARITIES] # Minimum lines number of a similarity. From ddf5738503e2ba6adb74359abf3b6e7aa81af420 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 3 Jun 2026 11:11:37 -0300 Subject: [PATCH 824/860] chardev: Don't unregister yank upon async path connection failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5c102ac9 ("chardev: Consolidate yank registration") has moved yank registration in the tcp_chr_connect_client_async() path to after the connection is successful. If qio_channel_socket_connect_sync() fails early, there will be no yank registered to be unregistered in the error path, leading to assert. Remove the now-extraneous unregister. Cc: qemu-stable@nongnu.org Fixes: 5c102ac9 ("chardev: Consolidate yank registration") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3528 Signed-off-by: Fabiano Rosas Reviewed-by: Marc-André Lureau Message-ID: <20260603141137.1108963-1-farosas@suse.de> --- chardev/char-socket.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index b629575fcf..81bac42a15 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -1128,11 +1128,6 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) if (qio_task_propagate_error(task, &err)) { tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); - if (s->registered_yank) { - yank_unregister_function(CHARDEV_YANK_INSTANCE(chr->label), - char_socket_yank_iochannel, - QIO_CHANNEL(sioc)); - } check_report_connect_error(chr, err); goto cleanup; } From 418396be8013386a81f8d8d89ac0effcf03a64b6 Mon Sep 17 00:00:00 2001 From: Warisjeet Singh Date: Mon, 24 Aug 2026 12:47:26 -0400 Subject: [PATCH 825/860] hw/display/vga: fix text-mode OOB write after a graphics surface switch vga_draw_text() decides whether the console surface needs a resize from its geometry cache, but none of the cache terms observe the graphics renderer having replaced the console surface in between: - last_width/last_height are shared with vga_draw_graphic(), which stores them in pixels while the text path stores characters; - last_depth stays 0 for legacy (non-VBE) graphics modes, because vga_get_bpp() only reports a depth when VBE is enabled, so the "s->last_depth" term that normally forces a resize after a graphics frame does not fire. So a graphics frame that shrinks the console surface (e.g. 80x25 pixels) followed by a text frame with matching character geometry (80x25 chars) skips the resize, and the glyph loop then paints width*cw x height*cheight pixels into the smaller surface, out of bounds, with guest-controlled (DAC palette) values, on every display refresh. Separate the geometry cache per renderer: text paths (vga_draw_text, vga_update_text, and the text handling in vga_invalidate_display / vga_common_reset) now only manipulate last_text_{width,height}, in characters; last_{width,height} become graphics-only, in pixels. Additionally, make the text path compare the pixel size it is about to paint against the console surface's actual dimensions. The surface check is the load-bearing term: caches in either unit cannot see the other renderer swapping the surface, the surface can. Fixes: CVE-2026-77913 Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4215 Cc: qemu-stable@nongnu.org Signed-off-by: Warisjeet Singh (sin99xx) Message-ID: --- hw/display/vga.c | 37 ++++++++++++++++++++++--------------- hw/display/vga_int.h | 3 ++- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/hw/display/vga.c b/hw/display/vga.c index da0c331486..cb0e28b79b 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -1241,7 +1241,10 @@ static void vga_draw_text(VGACommonState *s, int full_update) return; } - if (width != s->last_width || height != s->last_height || + if (surface == NULL || + surface_width(surface) != width * cw || + surface_height(surface) != height * cheight || + width != s->last_text_width || height != s->last_text_height || cw != s->last_cw || cheight != s->last_ch || s->last_depth) { s->last_scr_width = width * cw; s->last_scr_height = height * cheight; @@ -1249,8 +1252,8 @@ static void vga_draw_text(VGACommonState *s, int full_update) surface = qemu_console_surface(s->con); qemu_console_text_resize(s->con, width, height); s->last_depth = 0; - s->last_width = width; - s->last_height = height; + s->last_text_width = width; + s->last_text_height = height; s->last_ch = cheight; s->last_cw = cw; full_update = 1; @@ -1845,6 +1848,8 @@ static void vga_invalidate_display(void *opaque) s->last_width = -1; s->last_height = -1; + s->last_text_width = -1; + s->last_text_height = -1; } void vga_common_reset(VGACommonState *s) @@ -1887,6 +1892,8 @@ void vga_common_reset(VGACommonState *s) s->last_ch = 0; s->last_width = 0; s->last_height = 0; + s->last_text_width = 0; + s->last_text_height = 0; s->last_scr_width = 0; s->last_scr_height = 0; s->cursor_start = 0; @@ -1938,8 +1945,8 @@ static void vga_update_text(void *opaque, uint32_t *chardata) s->graphic_mode = graphic_mode; full_update = 1; } - if (s->last_width == -1) { - s->last_width = 0; + if (s->last_text_width == -1) { + s->last_text_width = 0; full_update = 1; } @@ -1978,15 +1985,15 @@ static void vga_update_text(void *opaque, uint32_t *chardata) break; } - if (width != s->last_width || height != s->last_height || + if (width != s->last_text_width || height != s->last_text_height || cw != s->last_cw || cheight != s->last_ch) { s->last_scr_width = width * cw; s->last_scr_height = height * cheight; qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); qemu_console_text_resize(s->con, width, height); s->last_depth = 0; - s->last_width = width; - s->last_height = height; + s->last_text_width = width; + s->last_text_height = height; s->last_ch = cheight; s->last_cw = cw; full_update = 1; @@ -2071,22 +2078,22 @@ static void vga_update_text(void *opaque, uint32_t *chardata) } /* Display a message */ - s->last_width = 60; - s->last_height = height = 3; + s->last_text_width = 60; + s->last_text_height = height = 3; qemu_console_text_set_cursor(s->con, -1, -1); - qemu_console_text_resize(s->con, s->last_width, height); + qemu_console_text_resize(s->con, s->last_text_width, height); - for (dst = chardata, i = 0; i < s->last_width * height; i ++) + for (dst = chardata, i = 0; i < s->last_text_width * height; i ++) *dst++ = ' '; size = strlen(msg_buffer); - width = (s->last_width - size) / 2; - dst = chardata + s->last_width + width; + width = (s->last_text_width - size) / 2; + dst = chardata + s->last_text_width + width; for (i = 0; i < size; i ++) *dst++ = ATTR2CHTYPE(msg_buffer[i], QEMU_COLOR_BLUE, QEMU_COLOR_BLACK, 1); - qemu_console_text_update(s->con, 0, 0, s->last_width, height); + qemu_console_text_update(s->con, 0, 0, s->last_text_width, height); } static uint64_t vga_mem_read(void *opaque, hwaddr addr, diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 5664317ecd..ca69ae9815 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -122,7 +122,8 @@ typedef struct VGACommonState { uint32_t plane_updated; uint32_t last_line_offset; uint8_t last_cw, last_ch; - uint32_t last_width, last_height; /* in chars or pixels */ + uint32_t last_width, last_height; /* in pixels (graphics renderer) */ + uint32_t last_text_width, last_text_height; /* in chars (text renderer) */ uint32_t last_scr_width, last_scr_height; /* in pixels */ uint32_t last_depth; /* in bits */ bool last_byteswap; From 14f2511f5952517a7daae6dc5986e8439f2bbb66 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 25 Aug 2026 16:28:47 +0900 Subject: [PATCH 826/860] hw/display/virtio-gpu: Avoid creating empty udmabuf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The virtio specification allows creating a blob without backing storage attached. However, virtio-gpu attempts to create an empty udmabuf for such a blob. The ioctl fails with EINVAL and emits a spurious warning. Avoid the invalid ioctl. Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Fixes: f66767f75c9c ("virtio-gpu: add virtio-gpu/blob vmstate subsection") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260825-dmabuf-v2-1-b3d64d3b9a0e@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 102 +++++++++++++++++++++------------------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 9eb010082d..50c4dcd408 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -363,27 +363,29 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g, res->resource_id = cblob.resource_id; res->blob_size = cblob.size; - ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob), - cmd, &res->addrs, &res->iov, - &res->iov_cnt); - if (ret < 0) { - cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; - g_free(res); - return; + if (cblob.nr_entries) { + ret = virtio_gpu_create_mapping_iov(g, cblob.nr_entries, sizeof(cblob), + cmd, &res->addrs, &res->iov, + &res->iov_cnt); + if (ret < 0) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + g_free(res); + return; + } + + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: backing storage smaller than blob size\n", + __func__); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } + + virtio_gpu_init_udmabuf(res); } - if (res->iov_cnt > 0 && - iov_size(res->iov, res->iov_cnt) < res->blob_size) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: backing storage smaller than blob size\n", - __func__); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; - virtio_gpu_cleanup_mapping(g, res); - g_free(res); - return; - } - - virtio_gpu_init_udmabuf(res); QTAILQ_INSERT_HEAD(&g->reslist, res, next); } @@ -1389,8 +1391,6 @@ static bool virtio_gpu_load_restore_mapping(VirtIOGPU *g, } } - QTAILQ_INSERT_HEAD(&g->reslist, res, next); - g->hostmem += res->hostmem; return true; } @@ -1469,6 +1469,8 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } + QTAILQ_INSERT_HEAD(&g->reslist, res, next); + g->hostmem += hostmem; resource_id = qemu_get_be32(f); } @@ -1528,36 +1530,38 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, res->blob_size = qemu_get_be32(f); res->iov_cnt = qemu_get_be32(f); - res->addrs = g_try_new(uint64_t, res->iov_cnt); - res->iov = g_try_new(struct iovec, res->iov_cnt); - if (res->iov_cnt && (!res->addrs || !res->iov)) { - g_free(res->addrs); - g_free(res->iov); - g_free(res); - return -EINVAL; + if (res->iov_cnt) { + res->addrs = g_try_new(uint64_t, res->iov_cnt); + res->iov = g_try_new(struct iovec, res->iov_cnt); + if (!res->addrs || !res->iov) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + + /* read data */ + for (i = 0; i < res->iov_cnt; i++) { + res->addrs[i] = qemu_get_be64(f); + res->iov[i].iov_len = qemu_get_be32(f); + } + + if (iov_size(res->iov, res->iov_cnt) < res->blob_size) { + g_free(res->addrs); + g_free(res->iov); + g_free(res); + return -EINVAL; + } + + if (!virtio_gpu_load_restore_mapping(g, res)) { + g_free(res); + return -EINVAL; + } + + virtio_gpu_init_udmabuf(res); } - /* read data */ - for (i = 0; i < res->iov_cnt; i++) { - res->addrs[i] = qemu_get_be64(f); - res->iov[i].iov_len = qemu_get_be32(f); - } - - if (res->iov_cnt > 0 && - iov_size(res->iov, res->iov_cnt) < res->blob_size) { - g_free(res->addrs); - g_free(res->iov); - g_free(res); - return -EINVAL; - } - - if (!virtio_gpu_load_restore_mapping(g, res)) { - g_free(res); - return -EINVAL; - } - - virtio_gpu_init_udmabuf(res); - + QTAILQ_INSERT_HEAD(&g->reslist, res, next); resource_id = qemu_get_be32(f); } From 3f163489c6efe8fb0de8e18097773f955ebaaa31 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 25 Aug 2026 16:28:48 +0900 Subject: [PATCH 827/860] hw/display/virtio-gpu: Avoid mmap() for empty blob MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling mmap() for an empty blob fails with EINVAL, causing QEMU to emit a spurious warning. Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260825-dmabuf-v2-2-b3d64d3b9a0e@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu-udmabuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 816f52a514..ba02ba9e86 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -139,7 +139,7 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) if (res->iov_cnt == 1 && res->iov[0].iov_len < 4096) { pdata = res->iov[0].iov_base; - } else { + } else if (res->blob_size) { virtio_gpu_create_udmabuf(res); if (res->dmabuf_fd < 0) { return; From cd71534b225c17c75ca745f13084abff12536f8b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 25 Aug 2026 16:28:49 +0900 Subject: [PATCH 828/860] hw/display/virtio-gpu: Propagate udmabuf errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagate udmabuf errors so that the requested operation will be canceled instead of producing an incomplete result and the user can notice the failure. Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Fixes: f66767f75c9c ("virtio-gpu: add virtio-gpu/blob vmstate subsection") Fixes: 4ae1c5c7d6f3 ("hw/display/virtio-gpu: Initialize blob mapping for ATTACH_BACKING") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260825-dmabuf-v2-3-b3d64d3b9a0e@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu-udmabuf-stubs.c | 3 ++- hw/display/virtio-gpu-udmabuf.c | 8 +++++--- hw/display/virtio-gpu.c | 18 ++++++++++++++---- include/hw/virtio/virtio-gpu.h | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/hw/display/virtio-gpu-udmabuf-stubs.c b/hw/display/virtio-gpu-udmabuf-stubs.c index 85d03935a3..0883bf05fa 100644 --- a/hw/display/virtio-gpu-udmabuf-stubs.c +++ b/hw/display/virtio-gpu-udmabuf-stubs.c @@ -7,9 +7,10 @@ bool virtio_gpu_have_udmabuf(void) return false; } -void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) +bool virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) { /* nothing (stub) */ + return false; } void virtio_gpu_fini_udmabuf(VirtIOGPU *g, struct virtio_gpu_simple_resource *res) diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index ba02ba9e86..c230509852 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -131,7 +131,7 @@ bool virtio_gpu_have_udmabuf(void) return memfd_backend; } -void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) +bool virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) { void *pdata = NULL; @@ -142,17 +142,19 @@ void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res) } else if (res->blob_size) { virtio_gpu_create_udmabuf(res); if (res->dmabuf_fd < 0) { - return; + return false; } virtio_gpu_remap_udmabuf(res); if (!res->remapped) { virtio_gpu_destroy_udmabuf(res); - return; + return false; } pdata = res->remapped; } res->blob = pdata; + + return true; } static void virtio_gpu_free_dmabuf(VirtIOGPU *g, VGPUDMABuf *dmabuf) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 50c4dcd408..7f3301a9ac 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -383,7 +383,12 @@ static void virtio_gpu_resource_create_blob(VirtIOGPU *g, return; } - virtio_gpu_init_udmabuf(res); + if (!virtio_gpu_init_udmabuf(res)) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return; + } } QTAILQ_INSERT_HEAD(&g->reslist, res, next); @@ -1045,8 +1050,9 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, return; } - if (!res->image) { - virtio_gpu_init_udmabuf(res); + if (!res->image && !virtio_gpu_init_udmabuf(res)) { + cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; + virtio_gpu_cleanup_mapping(g, res); } } @@ -1558,7 +1564,11 @@ static int virtio_gpu_blob_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } - virtio_gpu_init_udmabuf(res); + if (!virtio_gpu_init_udmabuf(res)) { + virtio_gpu_cleanup_mapping(g, res); + g_free(res); + return -EINVAL; + } } QTAILQ_INSERT_HEAD(&g->reslist, res, next); diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 220231ec9d..69b5ee2e38 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -388,7 +388,7 @@ bool virtio_gpu_scanout_blob_to_fb(struct virtio_gpu_framebuffer *fb, /* virtio-gpu-udmabuf.c */ bool virtio_gpu_have_udmabuf(void); -void virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res); +bool virtio_gpu_init_udmabuf(struct virtio_gpu_simple_resource *res); void virtio_gpu_fini_udmabuf(VirtIOGPU *g, struct virtio_gpu_simple_resource *res); int virtio_gpu_update_dmabuf(VirtIOGPU *g, From d3c2da174ca69161eafeb82d98d0117150017909 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 25 Aug 2026 16:28:50 +0900 Subject: [PATCH 829/860] hw/display/virtio-gpu: Check cursor data presence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reject a blob that lacks the backing storage for VIRTIO_GPU_CMD_UPDATE_CURSOR. Fixes: bdd53f739273 ("virtio-gpu: Update cursor data using blob") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260825-dmabuf-v2-4-b3d64d3b9a0e@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 7f3301a9ac..01549d29d8 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -63,8 +63,8 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, } data = pixman_image_get_data(res->image); } else { - if (res->blob_size < (s->current_cursor->width * - s->current_cursor->height * 4)) { + if (!res->iov || res->blob_size < (s->current_cursor->width * + s->current_cursor->height * 4)) { return; } data = res->blob; From 884363589b68049cb344b8f8089464bada32a8e7 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 25 Aug 2026 16:28:51 +0900 Subject: [PATCH 830/860] hw/display/virtio-gpu: Validate resource per command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit virtio_gpu_find_check_resource() checks if the resource has backing storage if require_backing is true, but the condition conflates backing storage attachment with host representation; it checks !res->iov || (!res->image && !res->blob), but !res->iov is sufficient. Furthermore, its callers passing true as require_backing have different requirements: - virtio_gpu_transfer_to_host_2d() requires a non-blob with backing storage. - virtio_gpu_set_scanout() requires a non-blob but does not require backing storage. - virtio_gpu_set_scanout_blob() requires a blob with backing storage. - virtio_gpu_resource_detach_backing() accepts any resource. Remove the require_backing parameter and open-code checks appropriate for each function instead. Fixes: 25c001a40346 ("virtio-gpu: Add virtio_gpu_find_check_resource") Fixes: e0933d91b1cd ("virtio-gpu: Add virtio_gpu_resource_create_blob") Fixes: 32db3c63ae11 ("virtio-gpu: Add virtio_gpu_set_scanout_blob") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-ID: <20260825-dmabuf-v2-5-b3d64d3b9a0e@rsg.ci.i.u-tokyo.ac.jp> --- hw/display/virtio-gpu.c | 66 ++++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 01549d29d8..55a1c7f80f 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -37,7 +37,6 @@ static struct virtio_gpu_simple_resource * virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, - bool require_backing, const char *caller, uint32_t *error); static void virtio_gpu_reset_bh(void *opaque); @@ -50,8 +49,7 @@ void virtio_gpu_update_cursor_data(VirtIOGPU *g, uint32_t pixels; void *data; - res = virtio_gpu_find_check_resource(g, resource_id, false, - __func__, NULL); + res = virtio_gpu_find_check_resource(g, resource_id, __func__, NULL); if (!res) { return; } @@ -128,7 +126,6 @@ virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id) static struct virtio_gpu_simple_resource * virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, - bool require_backing, const char *caller, uint32_t *error) { struct virtio_gpu_simple_resource *res; @@ -143,17 +140,6 @@ virtio_gpu_find_check_resource(VirtIOGPU *g, uint32_t resource_id, return NULL; } - if (require_backing) { - if (!res->iov || (!res->image && !res->blob)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: no backing storage %d\n", - caller, resource_id); - if (error) { - *error = VIRTIO_GPU_RESP_ERR_UNSPEC; - } - return NULL; - } - } - return res; } @@ -474,9 +460,24 @@ static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g, virtio_gpu_t2d_bswap(&t2d); trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id); - res = virtio_gpu_find_check_resource(g, t2d.resource_id, true, + res = virtio_gpu_find_check_resource(g, t2d.resource_id, __func__, &cmd->error); - if (!res || res->blob) { + if (!res) { + return; + } + + if (!res->image) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource %d is a blob\n", + __func__, t2d.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (!res->iov) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: resource %d has no backing storage\n", + __func__, t2d.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } @@ -533,7 +534,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g, trace_virtio_gpu_cmd_res_flush(rf.resource_id, rf.r.width, rf.r.height, rf.r.x, rf.r.y); - res = virtio_gpu_find_check_resource(g, rf.resource_id, false, + res = virtio_gpu_find_check_resource(g, rf.resource_id, __func__, &cmd->error); if (!res) { return; @@ -771,12 +772,19 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, return; } - res = virtio_gpu_find_check_resource(g, ss.resource_id, true, + res = virtio_gpu_find_check_resource(g, ss.resource_id, __func__, &cmd->error); if (!res) { return; } + if (!res->image) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: resource %d is a blob\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + fb.format = pixman_image_get_format(res->image); bytes_pp = virtio_gpu_format_bytes_pp(fb.format); fb.width = pixman_image_get_width(res->image); @@ -866,12 +874,28 @@ static void virtio_gpu_set_scanout_blob(VirtIOGPU *g, return; } - res = virtio_gpu_find_check_resource(g, ss.resource_id, true, + res = virtio_gpu_find_check_resource(g, ss.resource_id, __func__, &cmd->error); if (!res) { return; } + if (res->image) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: resource %d is not a blob\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + + if (!res->iov) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: resource %d has no backing storage\n", + __func__, ss.resource_id); + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; + return; + } + if (!virtio_gpu_scanout_blob_to_fb(&fb, &ss, res->blob_size)) { cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; return; @@ -1067,7 +1091,7 @@ virtio_gpu_resource_detach_backing(VirtIOGPU *g, virtio_gpu_bswap_32(&detach, sizeof(detach)); trace_virtio_gpu_cmd_res_back_detach(detach.resource_id); - res = virtio_gpu_find_check_resource(g, detach.resource_id, true, + res = virtio_gpu_find_check_resource(g, detach.resource_id, __func__, &cmd->error); if (!res) { return; From 0846740652d1333369265fa075708b5a386c5ff6 Mon Sep 17 00:00:00 2001 From: Christian Quante Date: Tue, 25 Aug 2026 09:51:26 +0200 Subject: [PATCH 831/860] hw/input/ps2: answer unknown mouse commands with a resend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ps2_write_mouse() ends its command switch with a bare "default: break;", so an unknown command draws no reply at all. A real PS/2 device answers every byte it is given -- ACK (0xFA) when it understood one, resend (0xFE) when it did not -- and a guest that gets nothing back is left waiting out its reply timeout. The keyboard path in the same file has answered unknown commands with KBD_REPLY_RESEND since commit 06b3611fc2a3 ("ps2: reject unknown commands, instead of blindly accepting them"). Two guests were measured on this. OS/2 probes the mouse with the vendor command 0xBB, which QEMU does not implement, and then polls the status port until its own timeout runs out. On a Warp 3 guest that wait costs about 25 ms of every boot under TCG, and 2.1 s under KVM, where each of those polls leaves the guest. With this patch the wait ends on the first read: the guest takes the same error path an unexpected reply would, and does not retry. Linux runs into two of them while probing the mouse: the ALPS probe sends 0xEC (reset wrap mode), which ps2_write_mouse() only answers while the mouse is in wrap mode, and the TrackPoint probe sends 0xE1. Each costs libps2 a 200 ms reply timeout. Timing the psmouse detection from a mark written to /dev/kmsg to the kernel's "input:" line, three boots each of a 6.18.35 kernel under TCG: 426.7/428.8/441.6 ms without this patch, 21.4/21.6/21.2 ms with it. The mouse is detected identically either way; only the error the probe ends in changes, from -EIO (nothing came back at all) to -EPROTO (libps2 gives up after its second attempt). The specification's second stage -- 0xFC (Error) when the byte after a rejected one is invalid as well -- is deliberately left out. It would need state that has to survive migration, no guest is known to test for it, and the keyboard path does without it as well. Cc: qemu-stable@nongnu.org Signed-off-by: Christian Quante Reviewed-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20260825075127.34876-2-christian@quante.one> --- hw/input/ps2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 5516eb262d..e8300d2d83 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -73,6 +73,7 @@ #define AUX_SET_DEFAULT 0xF6 #define AUX_RESET 0xFF /* Reset aux device */ #define AUX_ACK 0xFA /* Command byte ACK. */ +#define AUX_RESEND 0xFE /* Command NACK, send the cmd again */ #define MOUSE_STATUS_REMOTE 0x40 #define MOUSE_STATUS_ENABLED 0x20 @@ -955,6 +956,11 @@ void ps2_write_mouse(PS2MouseState *s, int val) s->mouse_type); break; default: + /* + * A PS/2 device answers every command it is given; an unknown + * one draws a resend. + */ + ps2_queue(ps2, AUX_RESEND); break; } break; From a88191a0caecdfba440682e3c120b439681ccaca Mon Sep 17 00:00:00 2001 From: Christian Quante Date: Tue, 25 Aug 2026 09:51:27 +0200 Subject: [PATCH 832/860] hw/input/ps2: say why unknown keyboard commands draw a resend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The keyboard path has answered unknown commands with KBD_REPLY_RESEND since commit 06b3611fc2a3 ("ps2: reject unknown commands, instead of blindly accepting them"), but never said why. Give it the comment the mouse path just gained, so the reasoning is written down in both places. Suggested-by: Akihiko Odaki Signed-off-by: Christian Quante Reviewed-by: Marc-André Lureau Reviewed-by: Akihiko Odaki Message-ID: <20260825075127.34876-3-christian@quante.one> --- hw/input/ps2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index e8300d2d83..01af4350b3 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -647,6 +647,10 @@ void ps2_write_keyboard(PS2KbdState *s, int val) ps2_cqueue_1(ps2, KBD_REPLY_ACK); break; default: + /* + * A PS/2 device answers every command it is given; an unknown + * one draws a resend. + */ ps2_cqueue_1(ps2, KBD_REPLY_RESEND); break; } From 6c712a86f6c8d8bdd8a3ba6b39cbcd9550668c36 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 15:35:29 +0200 Subject: [PATCH 833/860] hw/ide: reject an unsupported CHS translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_set_sector() divides by (s->heads * s->sectors) when the drive is addressed in CHS mode. Both come from the guest via INITIALIZE DEVICE PARAMETERS, and cmd_specify() stored them without any check, so a guest asking for zero sectors per logical track killed QEMU with SIGFPE on the completion of the first CHS read or write. s->heads is safe, as the command passes a heads-1 value. The count has an upper bound as well. The legacy sector count register is eight bits wide, but handle_cmd() takes the count from a 16 bit field of the register FIS, so an AHCI guest can ask for up to 65535 sectors per track, and the CHS branch of ide_get_sector() then overflows the int it multiplies cylinder, heads and sectors in. ATA-5 6.2 numbers CHS sectors from one and ATA-2 D.2.8 limits IDENTIFY DEVICE word 56 to 1 through 255, so neither end is a translation a device may accept. ATA-5 8.16.6 requires an unsupported one to be reported as an aborted command: do that, leave the translation in effect alone, and refuse the value rather than checking it at every use. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Cc: qemu-stable@nongnu.org Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command") Reported-by: Zheyu Ma Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2399 Signed-off-by: Denis V. Lunev Reviewed-by: Philippe Mathieu-Daudé --- hw/ide/core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index fb9bf11b45..747fa71677 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1655,14 +1655,21 @@ static bool cmd_check_power_mode(IDEState *s, uint8_t cmd) /* INITIALIZE DEVICE PARAMETERS */ static bool cmd_specify(IDEState *s, uint8_t cmd) { - if (s->blk && s->drive_kind != IDE_CD) { - s->heads = (s->select & (ATA_DEV_HS)) + 1; - s->sectors = s->nsector; - ide_bus_set_irq(s->bus); - } else { + if (!s->blk || s->drive_kind == IDE_CD) { ide_abort_command(s); + return true; } + /* ATA-2 D.2.8 limits IDENTIFY DEVICE word 56, and the count, to 1..255 */ + if (s->nsector == 0 || s->nsector > 255) { + ide_abort_command(s); + return true; + } + + s->heads = (s->select & (ATA_DEV_HS)) + 1; + s->sectors = s->nsector; + ide_bus_set_irq(s->bus); + return true; } From 0905ef5b6d483da0e27831152fdde059595244b9 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 4 Aug 2026 18:34:44 +0200 Subject: [PATCH 834/860] tests/qtest/ide-test: cover a CHS translation with zero sectors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ask for zero sectors per logical track via INITIALIZE DEVICE PARAMETERS and check that the command is aborted, that IDENTIFY DEVICE still reports the translation that was in effect before, and that a CHS read then completes normally rather than killing QEMU with SIGFPE. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 62 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index b36b0f8875..99aa5e039c 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -95,6 +95,7 @@ enum { enum { CMD_DSM = 0x06, + CMD_READ = 0x20, /* READ SECTOR(S) */ CMD_DIAGNOSE = 0x90, CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */ CMD_READ_DMA = 0xc8, @@ -1194,6 +1195,66 @@ static void cdrom_read_impl(int nblocks, unsigned flags) free_pci_device(dev); } +/* Zero sectors per track has to abort (ATA-5 8.16.6), not divide by zero */ +static void test_specify_zero_sectors(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + uint8_t data; + int i; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + qpci_io_writeb(dev, ide_bar, reg_nsectors, 0); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), ERR); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_error), ABRT); + + /* The refused request has to leave the default translation in effect */ + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + g_assert_cmpint(buf[55], ==, 16); + g_assert_cmpint(buf[56], ==, 63); + + /* READ SECTOR(S) of CHS 0/0/1, which used to crash QEMU */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_high, 0); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ); + + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ); + assert_bit_clear(data, ERR | DF); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR | DF | DRQ); + + /* A supported translation is still accepted */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 32); + qpci_io_writeb(dev, ide_bar, reg_device, 7); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR); + + ide_test_quit(qts); + free_pci_device(dev); +} + static void test_cdrom_pio(void) { cdrom_read_impl(1, CDROM_PIO); @@ -1265,6 +1326,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/ide/read_native", test_specify); + qtest_add_func("/ide/specify/zero_sectors", test_specify_zero_sectors); qtest_add_func("/ide/identify", test_identify); From 5e16adf5550e50e7d792d7a328f4bc0b17a53673 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 15:35:23 +0200 Subject: [PATCH 835/860] tests/qtest/libqos/ahci: allow a count and an expected error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A command that transfers no data can still take an argument in the count register of the register FIS, and a test may well expect such a command to be aborted. AHCICommand is private to the library, so add two setters: ahci_command_set_count() writes the count of a non-data command, and ahci_command_expect_error() records the error register bits the command is expected to complete with, which is what ahci_atapi_test_ready() does inline for a sense key today. INITIALIZE DEVICE PARAMETERS is the first user of both, so describe it in the command properties table as well. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/libqos/ahci.c | 14 ++++++++++++++ tests/qtest/libqos/ahci.h | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c index e3019a1eca..50b63239c2 100644 --- a/tests/qtest/libqos/ahci.c +++ b/tests/qtest/libqos/ahci.c @@ -74,6 +74,7 @@ AHCICommandProp ahci_command_properties[] = { { .cmd = CMD_READ_MAX, .lba28 = true }, { .cmd = CMD_READ_MAX_EXT, .lba48 = true }, { .cmd = CMD_FLUSH_CACHE, .data = false }, + { .cmd = CMD_INIT_DP, .data = false }, { .cmd = CMD_PACKET, .data = true, .size = 16, .atapi = true, .pio = true }, { .cmd = CMD_PACKET_ID, .data = true, .pio = true, @@ -1180,6 +1181,19 @@ void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size) ahci_command_set_sizes(cmd, cmd->xbytes, prd_size); } +/* For a no-data command, whose count carries an argument of its own */ +void ahci_command_set_count(AHCICommand *cmd, uint16_t count) +{ + g_assert(!cmd->props->data); + cmd->fis.count = count; +} + +void ahci_command_expect_error(AHCICommand *cmd, uint8_t err) +{ + cmd->interrupts |= AHCI_PX_IS_TFES; + cmd->errors |= err; +} + void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer, uint64_t xbytes, unsigned prd_size) { diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h index 2a48a7523d..6d861c79ee 100644 --- a/tests/qtest/libqos/ahci.h +++ b/tests/qtest/libqos/ahci.h @@ -278,6 +278,7 @@ enum { CMD_READ_MAX = 0xF8, CMD_READ_MAX_EXT = 0x27, CMD_FLUSH_CACHE = 0xE7, + CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */ CMD_IDENTIFY = 0xEC, CMD_PACKET = 0xA0, CMD_PACKET_ID = 0xA1, @@ -324,6 +325,9 @@ enum { #define ATA_DEVICE_DRIVE 0x10 #define ATA_DEVICE_HEAD 0x0F +/* ATA error register bits */ +#define ATA_ERR_ABRT 0x04 + /*** Structures ***/ typedef struct AHCIPortQState { @@ -638,6 +642,8 @@ void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes); void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size); void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, unsigned prd_size); +void ahci_command_set_count(AHCICommand *cmd, uint16_t count); +void ahci_command_expect_error(AHCICommand *cmd, uint8_t err); void ahci_command_set_acmd(AHCICommand *cmd, void *acmd); void ahci_command_enable_atapi_dma(AHCICommand *cmd); void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer, From d6960ecdded35b2d4ef8b23763d19038b2d190b6 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 15:35:23 +0200 Subject: [PATCH 836/860] tests/qtest/ahci: cover the sector count of INITIALIZE DEVICE PARAMETERS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sector count register of a legacy port is eight bits wide, so ide-test can only reach the lower end of the range the command has to refuse. A register FIS carries a 16 bit count, which leaves AHCI as the only way to ask for a translation of 256 sectors per logical track or more. Ask for 0, 256 and 65535 sectors and expect each to be aborted, then ask for 32 and expect it to be accepted, so that the check cannot pass by refusing everything. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ahci-test.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 71d23fe56a..30d7005626 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -905,6 +905,30 @@ static void ahci_test_flush(AHCIQState *ahci) ahci_test_nondata(ahci, CMD_FLUSH_CACHE); } +static void ahci_test_specify(AHCIQState *ahci, uint16_t sectors, + bool supported) +{ + AHCICommand *cmd; + uint8_t port; + + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + cmd = ahci_command_create(CMD_INIT_DP); + ahci_command_set_count(cmd, sectors); + if (!supported) { + ahci_command_expect_error(cmd, ATA_ERR_ABRT); + } + ahci_command_commit(ahci, cmd, port); + ahci_command_issue(ahci, cmd); + if (!supported) { + ASSERT_BIT_SET(ahci_px_rreg(ahci, port, AHCI_PX_TFD), + AHCI_PX_TFD_STS_ERR); + } + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); +} + static void ahci_test_max(AHCIQState *ahci) { RegD2HFIS *d2h = g_malloc0(0x20); @@ -1012,6 +1036,21 @@ static void test_identify(void) ahci_shutdown(ahci); } +static void test_specify(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + + /* A register FIS carries 16 bits of count, the legacy ports only eight */ + ahci_test_specify(ahci, 0, false); + ahci_test_specify(ahci, 256, false); + ahci_test_specify(ahci, 0xffff, false); + ahci_test_specify(ahci, 32, true); + + ahci_shutdown(ahci); +} + /** * Fragmented DMA test: Perform a standard 4K DMA read/write * test, but make sure the physical regions are fragmented to @@ -2220,6 +2259,7 @@ int main(int argc, char **argv) qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma); qtest_add_func("/ahci/max", test_max); + qtest_add_func("/ahci/specify", test_specify); qtest_add_func("/ahci/reset/simple", test_reset); qtest_add_func("/ahci/reset/pending_callback", test_reset_pending_callback); From 0df27c66dc7271ee2e63636b5183c0b3acdf7eed Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 17:41:34 +0200 Subject: [PATCH 837/860] hw/ide: report the default CHS translation in IDENTIFY DEVICE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IDENTIFY DEVICE words 1, 3 and 6 describe the default CHS translation, and ATA-5 8.16.8 requires INITIALIZE DEVICE PARAMETERS to leave them alone; the translation in effect is described by words 54 to 56 instead. Words 3 and 6 were filled from s->heads and s->sectors, which the command replaces, so a guest that selected a translation of its own was told that its choice was what the drive came with, and could no longer find out the default. Word 1 is already right, as no command changes s->cylinders. Report s->drive_heads and s->drive_sectors, which ide_init_drive() keeps for exactly this, along with the retired word 4 derived from them. The CompactFlash data labels those words as the default geometry too, and INITIALIZE DEVICE PARAMETERS is accepted for CFA drives, so fix both. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Cc: qemu-stable@nongnu.org Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command") Signed-off-by: Denis V. Lunev Reviewed-by: Philippe Mathieu-Daudé --- hw/ide/core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 747fa71677..befbab9486 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -138,11 +138,12 @@ static void ide_identify(IDEState *s) memset(p, 0, sizeof(s->identify_data)); put_le16(p + 0, 0x0040); + /* Words 1, 3 and 6 describe the default translation (ATA-5 8.16.8) */ put_le16(p + 1, s->cylinders); - put_le16(p + 3, s->heads); - put_le16(p + 4, 512 * s->sectors); /* XXX: retired, remove ? */ + put_le16(p + 3, s->drive_heads); + put_le16(p + 4, 512 * s->drive_sectors); /* XXX: retired, remove ? */ put_le16(p + 5, 512); /* XXX: retired, remove ? */ - put_le16(p + 6, s->sectors); + put_le16(p + 6, s->drive_sectors); padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ put_le16(p + 20, 3); /* XXX: retired, remove ? */ put_le16(p + 21, 512); /* cache size in sectors */ @@ -332,8 +333,8 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 0, 0x848a); /* CF Storage Card signature */ put_le16(p + 1, s->cylinders); /* Default cylinders */ - put_le16(p + 3, s->heads); /* Default heads */ - put_le16(p + 6, s->sectors); /* Default sectors per track */ + put_le16(p + 3, s->drive_heads); /* Default heads */ + put_le16(p + 6, s->drive_sectors); /* Default sectors per track */ /* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */ /* *(p + 8) := nb_sectors -- see ide_cfata_identify_size */ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ From adf1152b3454673a19f84e830dce80203851864c Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 20 Aug 2026 09:37:11 +0200 Subject: [PATCH 838/860] hw/ide: name the retired IDENTIFY DEVICE words the device fills in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Words 4, 5 and 20 have carried an "XXX: retired, remove ?" since the device was written, and word 21 is labelled a cache size when it is a buffer size. ATA-4 8.12.13 retired words 4 and 5, 8.12.17 retired words 20 and 21, and ATA-5 keeps all four that way. Retired is not a reason to drop them. ATA-5 3.2.3.6 says a retired word that is still used shall have "the meaning or functionality as described in previous standards", and that is what these carry: ATA-1 9.9.3, 9.9.4 and 9.9.7 define the unformatted bytes per track and per sector and the buffer type, and the ATA-1 IDENTIFY table gives word 21 as the buffer size in 512 byte increments. Software old enough to read them gets what it expects, so answer the question rather than leave it open. Word 22 is obsolete rather than retired (ATA-4 8.12.18) and already carries its ATA-1 9.9.8 name, so leave it alone. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index befbab9486..292908fb1c 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -141,12 +141,12 @@ static void ide_identify(IDEState *s) /* Words 1, 3 and 6 describe the default translation (ATA-5 8.16.8) */ put_le16(p + 1, s->cylinders); put_le16(p + 3, s->drive_heads); - put_le16(p + 4, 512 * s->drive_sectors); /* XXX: retired, remove ? */ - put_le16(p + 5, 512); /* XXX: retired, remove ? */ + put_le16(p + 4, 512 * s->drive_sectors); /* ATA-1 unformatted bytes/trk */ + put_le16(p + 5, 512); /* ATA-1 unformatted bytes per sector */ put_le16(p + 6, s->drive_sectors); padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ - put_le16(p + 20, 3); /* XXX: retired, remove ? */ - put_le16(p + 21, 512); /* cache size in sectors */ + put_le16(p + 20, 3); /* ATA-1 buffer type: dual ported, read caching */ + put_le16(p + 21, 512); /* ATA-1 buffer size in 512 byte increments */ put_le16(p + 22, 4); /* ecc bytes */ padstr((char *)(p + 23), s->version, 8); /* firmware version */ padstr((char *)(p + 27), s->drive_model_str, 40); /* model */ From 747679ca5981f1d591dc53e076df8a39d3428061 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 17:43:26 +0200 Subject: [PATCH 839/860] hw/ide: factor out the IDENTIFY DEVICE current geometry words MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Words 54 to 58 of IDENTIFY DEVICE describe the CHS translation in effect and the capacity it addresses. Both ide_identify() and ide_cfata_identify() fill them the same way while building their cached data. Move them into ide_identify_chs(), so that the next change can refresh them in place once the translation changes, the way ide_identify_size() does for the capacity words. No functional change. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev Reviewed-by: Philippe Mathieu-Daudé --- hw/ide/core.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 292908fb1c..e5fd570575 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -110,6 +110,18 @@ static void put_le16(uint16_t *p, unsigned int v) *p = cpu_to_le16(v); } +static void ide_identify_chs(IDEState *s) +{ + uint16_t *p = (uint16_t *)s->identify_data; + unsigned int cur_sec = s->cylinders * s->heads * s->sectors; + + put_le16(p + 54, s->cylinders); + put_le16(p + 55, s->heads); + put_le16(p + 56, s->sectors); + put_le16(p + 57, cur_sec); + put_le16(p + 58, cur_sec >> 16); +} + static void ide_identify_size(IDEState *s) { uint16_t *p = (uint16_t *)s->identify_data; @@ -128,7 +140,6 @@ static void ide_identify_size(IDEState *s) static void ide_identify(IDEState *s) { uint16_t *p; - unsigned int oldsize; IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; p = (uint16_t *)s->identify_data; @@ -158,12 +169,7 @@ static void ide_identify(IDEState *s) put_le16(p + 51, 0x200); /* PIO transfer cycle */ put_le16(p + 52, 0x200); /* DMA transfer cycle */ put_le16(p + 53, 1 | (1 << 1) | (1 << 2)); /* words 54-58,64-70,88 are valid */ - put_le16(p + 54, s->cylinders); - put_le16(p + 55, s->heads); - put_le16(p + 56, s->sectors); - oldsize = s->cylinders * s->heads * s->sectors; - put_le16(p + 57, oldsize); - put_le16(p + 58, oldsize >> 16); + ide_identify_chs(s); if (s->mult_sectors) put_le16(p + 59, 0x100 | s->mult_sectors); /* *(p + 60) := nb_sectors -- see ide_identify_size */ @@ -321,7 +327,6 @@ static void ide_cfata_identify_size(IDEState *s) static void ide_cfata_identify(IDEState *s) { uint16_t *p; - uint32_t cur_sec; p = (uint16_t *)s->identify_data; if (s->identify_set) { @@ -329,8 +334,6 @@ static void ide_cfata_identify(IDEState *s) } memset(p, 0, sizeof(s->identify_data)); - cur_sec = s->cylinders * s->heads * s->sectors; - put_le16(p + 0, 0x848a); /* CF Storage Card signature */ put_le16(p + 1, s->cylinders); /* Default cylinders */ put_le16(p + 3, s->drive_heads); /* Default heads */ @@ -350,11 +353,7 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 51, 0x0002); /* PIO cycle timing mode */ put_le16(p + 52, 0x0001); /* DMA cycle timing mode */ put_le16(p + 53, 0x0003); /* Translation params valid */ - put_le16(p + 54, s->cylinders); /* Current cylinders */ - put_le16(p + 55, s->heads); /* Current heads */ - put_le16(p + 56, s->sectors); /* Current sectors */ - put_le16(p + 57, cur_sec); /* Current capacity */ - put_le16(p + 58, cur_sec >> 16); /* Current capacity */ + ide_identify_chs(s); /* Current C/H/S and capacity */ if (s->mult_sectors) /* Multiple sector setting */ put_le16(p + 59, 0x100 | s->mult_sectors); /* *(p + 60) := nb_sectors -- see ide_cfata_identify_size */ From b5542796b3cbe052daa3660d00ec1d4a1033e7ff Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 17:43:26 +0200 Subject: [PATCH 840/860] hw/ide: keep the IDENTIFY DEVICE current geometry in sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bit 0 of IDENTIFY DEVICE word 53 says that words 54 to 58 describe the CHS translation in effect, and ATA-5 8.16.8 has INITIALIZE DEVICE PARAMETERS set words 55 and 56 to the heads and sectors per track it was given. The data is built once and then cached, so those words kept describing whatever was in effect when a guest first asked for IDENTIFY DEVICE: the device reported one geometry while addressing the medium with another, and nothing reported an error. The revert SET FEATURES 0xCC asks for on the next reset left the same disagreement. Do not drop the cached data on a change, as parts of it are guest state rather than a description of the drive: SET FEATURES records the write cache setting in word 85, which ide_drive_post_load() reads back after migration. Refresh the affected words in place instead, the way ide_identify_size() does for the capacity words. An ATAPI device has no translation but does take SET FEATURES 0xCC, so leave its IDENTIFY PACKET DEVICE data alone, where those words differ. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command") Signed-off-by: Denis V. Lunev --- hw/ide/core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index e5fd570575..048655b2d0 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1360,6 +1360,10 @@ static void ide_reset(IDEState *s) s->reset_reverts = false; s->heads = s->drive_heads; s->sectors = s->drive_sectors; + /* An ATAPI device takes SET FEATURES 0xCC but has no translation */ + if (s->identify_set && s->drive_kind != IDE_CD) { + ide_identify_chs(s); + } } if (s->drive_kind == IDE_CFATA) s->mult_sectors = 0; @@ -1668,6 +1672,9 @@ static bool cmd_specify(IDEState *s, uint8_t cmd) s->heads = (s->select & (ATA_DEV_HS)) + 1; s->sectors = s->nsector; + if (s->identify_set) { + ide_identify_chs(s); + } ide_bus_set_irq(s->bus); return true; From 33db64cf28829103e6b60905666082a01d011fc8 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 4 Aug 2026 18:34:44 +0200 Subject: [PATCH 841/860] hw/ide: restore the power-on device state before loading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loading a snapshot reuses the IDEState of the machine it is loaded into: load_snapshot() resets the machine and then feeds the stream into the existing devices. The reset does not help, as ide_reset() restores the logical CHS translation only when the guest asked for power-on defaults to be reverted with SET FEATURES 0xCC. A guest that replaced the translation with INITIALIZE DEVICE PARAMETERS therefore keeps it across the load of a snapshot taken before it did, while the restored guest expects the geometry of that moment. Every CHS access then lands on a sector other than the one asked for, with no error reported. s->reset_reverts survives a load the same way. Add a pre_load restoring the defaults, which docs/devel/migration/main.rst recommends for state a stream need not carry, and which the following subsections rely on. The RESET_TYPE_SNAPSHOT_LOAD marking that reset would be another way to recognise the case, but no IDE controller can see it while they all use device_class_set_legacy_reset(). Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command") Signed-off-by: Denis V. Lunev --- hw/ide/core.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index 048655b2d0..28219f5ef8 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2863,6 +2863,18 @@ static int transfer_end_table_idx(EndTransferFunc *fn) return -1; } +static int ide_drive_pre_load(void *opaque) +{ + IDEState *s = opaque; + + /* The subsections below are sent only where the guest replaced these */ + s->heads = s->drive_heads; + s->sectors = s->drive_sectors; + s->reset_reverts = false; + + return 0; +} + static int ide_drive_post_load(void *opaque, int version_id) { IDEState *s = opaque; @@ -2986,6 +2998,7 @@ const VMStateDescription vmstate_ide_drive = { .name = "ide_drive", .version_id = 3, .minimum_version_id = 0, + .pre_load = ide_drive_pre_load, .post_load = ide_drive_post_load, .fields = (const VMStateField[]) { VMSTATE_INT32(mult_sectors, IDEState), From ba4a996631cd3882246e0892e805d398a63e9b09 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 4 Aug 2026 18:34:44 +0200 Subject: [PATCH 842/860] hw/ide: migrate the logical CHS translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit INITIALIZE DEVICE PARAMETERS lets a guest replace the logical CHS translation used to turn the CHS registers into an LBA, but s->heads and s->sectors were in no VMStateDescription. The destination rebuilt them from the drive configuration, so a guest that had selected one of its own kept addressing the disk in it while the device translated with the default, landing on sectors nobody asked for. Add a subsection for it, sent only when the guest replaced the default, so that migration to an older QEMU keeps working for every other guest. s->cylinders is left out, as no command changes it. Validate what is loaded in the existing post_load: ide_get_sector() multiplies by these fields and ide_set_sector() divides by them. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command") Signed-off-by: Denis V. Lunev --- hw/ide/core.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index 28219f5ef8..b074325afa 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2879,6 +2879,13 @@ static int ide_drive_post_load(void *opaque, int version_id) { IDEState *s = opaque; + /* Only a disk has a translation; an empty slot and ATAPI keep these zero */ + if (s->blk && s->drive_kind != IDE_CD && + (s->heads < 1 || s->heads > 16 || + s->sectors < 1 || s->sectors > 255)) { + return -EINVAL; + } + if (s->blk && s->identify_set) { blk_set_enable_write_cache(s->blk, !!(s->identify_data[85] & (1 << 5))); } @@ -2962,6 +2969,25 @@ static const VMStateDescription vmstate_ide_atapi_gesn_state = { } }; +static bool ide_chs_translation_needed(void *opaque) +{ + IDEState *s = opaque; + + return s->heads != s->drive_heads || s->sectors != s->drive_sectors; +} + +static const VMStateDescription vmstate_ide_drive_chs_translation = { + .name = "ide_drive/chs_translation", + .version_id = 1, + .minimum_version_id = 1, + .needed = ide_chs_translation_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(heads, IDEState), + VMSTATE_INT32(sectors, IDEState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ide_tray_state = { .name = "ide_drive/tray_state", .version_id = 1, @@ -3025,6 +3051,7 @@ const VMStateDescription vmstate_ide_drive = { }, .subsections = (const VMStateDescription * const []) { &vmstate_ide_drive_pio_state, + &vmstate_ide_drive_chs_translation, &vmstate_ide_tray_state, &vmstate_ide_atapi_gesn_state, NULL From 67a6d29c28cc1a2628b0e1bdeac93621a9d4d9cb Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 4 Aug 2026 18:34:44 +0200 Subject: [PATCH 843/860] hw/ide: migrate the power-on defaults revert flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SET FEATURES 0xCC asks for the next reset to revert to the power-on defaults, and 0x66 cancels that; ide_reset() restores the default CHS translation only when the flag is set. It was in no VMStateDescription, so it always arrived cleared. That was invisible while the destination had the default translation anyway. Now that the translation is migrated, the flag decides how long it stays in effect: without it, a reset after the migration reverts the geometry on the source and keeps it on the destination. Send it only alongside a translation the guest replaced. On the default geometry it reverts to what is already in effect, so such a guest need not lose its migration to an older QEMU over a subsection that changes nothing. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command") Signed-off-by: Denis V. Lunev --- hw/ide/core.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index b074325afa..5a5f072630 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2988,6 +2988,25 @@ static const VMStateDescription vmstate_ide_drive_chs_translation = { } }; +static bool ide_reset_reverts_needed(void *opaque) +{ + IDEState *s = opaque; + + return s->reset_reverts && ide_chs_translation_needed(opaque); +} + +/* The flag decides nothing on the default geometry, so it travels with one */ +static const VMStateDescription vmstate_ide_drive_reset_reverts = { + .name = "ide_drive/reset_reverts", + .version_id = 1, + .minimum_version_id = 1, + .needed = ide_reset_reverts_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(reset_reverts, IDEState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ide_tray_state = { .name = "ide_drive/tray_state", .version_id = 1, @@ -3052,6 +3071,7 @@ const VMStateDescription vmstate_ide_drive = { .subsections = (const VMStateDescription * const []) { &vmstate_ide_drive_pio_state, &vmstate_ide_drive_chs_translation, + &vmstate_ide_drive_reset_reverts, &vmstate_ide_tray_state, &vmstate_ide_atapi_gesn_state, NULL From ef64d2758e439871500c726c0d536b05d19c2532 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 4 Aug 2026 18:34:44 +0200 Subject: [PATCH 844/860] tests/qtest/ide-test: cover the CHS translation across migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both defects here are silent: the guest addresses the disk in the translation it selected while the device translates with another, so reads and writes land on a sector nobody asked for. Put a marker in each of the two candidate sectors and name the one the translation picked. CHS 0/1/1 is LBA 32 under 8 heads and 32 sectors per track and LBA 63 under the 16/63 the test drive is configured with; both markers are written by LBA, which no translation can influence. A translation the guest selected has to survive migration, and one it selected after a snapshot was taken must not outlive loading that snapshot back. The second needs a qcow2 image, so it is skipped without qemu-img. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 231 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 99aa5e039c..19e9734d3c 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -96,6 +96,7 @@ enum { enum { CMD_DSM = 0x06, CMD_READ = 0x20, /* READ SECTOR(S) */ + CMD_WRITE = 0x30, /* WRITE SECTOR(S) */ CMD_DIAGNOSE = 0x90, CMD_INIT_DP = 0x91, /* INITIALIZE DEVICE PARAMETERS */ CMD_READ_DMA = 0xc8, @@ -1255,6 +1256,233 @@ static void test_specify_zero_sectors(void) free_pci_device(dev); } +/* Addressed by LBA, so no translation can influence where it lands */ +static void ide_write_marker(QTestState *qts, QPCIDevice *dev, QPCIBar ide_bar, + uint32_t lba, const char *marker) +{ + uint16_t buf[256]; + uint8_t data; + int i; + + memset(buf, 0, sizeof(buf)); + memcpy(buf, marker, strlen(marker)); + + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, lba & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, (lba >> 8) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (lba >> 16) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_device, LBA | ((lba >> 24) & 0xf)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_WRITE); + + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ); + for (i = 0; i < 256; i++) { + qpci_io_writew(dev, ide_bar, reg_data, buf[i]); + } + data = ide_wait_clear(qts, BSY); + assert_bit_clear(data, ERR | DF | DRQ); + + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); + data = ide_wait_clear(qts, BSY); + assert_bit_clear(data, ERR | DF); +} + +/* The marker read back names the sector the translation selected */ +static void ide_read_chs_marker(QTestState *qts, QPCIDevice *dev, + QPCIBar ide_bar, uint8_t cyl_lo, uint8_t head, + uint8_t sector, char out[9]) +{ + uint16_t buf[256]; + uint8_t data; + int i; + + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, sector); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, cyl_lo); + qpci_io_writeb(dev, ide_bar, reg_lba_high, 0); + qpci_io_writeb(dev, ide_bar, reg_device, head & 0xf); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_READ); + + data = ide_wait_clear(qts, BSY); + assert_bit_set(data, DRQ); + assert_bit_clear(data, ERR | DF); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + data = ide_wait_clear(qts, BSY); + assert_bit_clear(data, ERR | DF | DRQ); + + memcpy(out, buf, 8); + out[8] = '\0'; +} + +static void ide_set_translation(QPCIDevice *dev, QPCIBar ide_bar, + uint8_t heads, uint8_t sectors) +{ + qpci_io_writeb(dev, ide_bar, reg_nsectors, sectors); + qpci_io_writeb(dev, ide_bar, reg_device, heads - 1); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_INIT_DP); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), ERR); +} + +/* CHS 0/1/1 is LBA 32 under 8/32, and LBA 63 under the drive's own 16/63 */ +#define CHS_MARKER_CUSTOM "CUSTOM__" +#define CHS_MARKER_DEFAULT "DEFAULT_" + +static void ide_prepare_markers(QTestState *qts, QPCIDevice *dev, + QPCIBar ide_bar) +{ + ide_write_marker(qts, dev, ide_bar, 32, CHS_MARKER_CUSTOM); + ide_write_marker(qts, dev, ide_bar, 63, CHS_MARKER_DEFAULT); +} + +static void ide_hmp_quiet(QTestState *qts, const char *command) +{ + g_autofree char *out = qtest_hmp(qts, "%s", command); + + g_assert_cmpstr(out, ==, ""); +} + +static char *ide_migration_status(QTestState *qts) +{ + QDict *ret; + char *status; + + ret = qtest_qmp_assert_success_ref(qts, "{ 'execute': 'query-migrate' }"); + g_assert(qdict_haskey(ret, "status")); + status = g_strdup(qdict_get_str(ret, "status")); + qobject_unref(ret); + + return status; +} + +/* Waiting for the other side's event would hang if it refuses the stream */ +static void ide_migration_wait(QTestState *qts, const char *expected) +{ + while (true) { + g_autofree char *status = ide_migration_status(qts); + + if (g_str_equal(status, expected)) { + return; + } + if (!g_str_equal(status, "setup") && !g_str_equal(status, "active") && + !g_str_equal(status, "device")) { + fprintf(stderr, "Migration status is %s, expected %s\n", + status, expected); + g_assert_not_reached(); + } + g_usleep(5000); + } +} + +static void ide_migrate(QTestState *src, QTestState *dst, const char *uri) +{ + qtest_qmp_assert_success(src, "{ 'execute': 'migrate'," + " 'arguments': { 'uri': %s } }", uri); + qtest_qmp_eventwait(src, "STOP"); + ide_migration_wait(src, "completed"); + qtest_qmp_eventwait(dst, "RESUME"); +} + +/* A translation the guest selected has to survive migration */ +static void test_migrate_chs_translation(void) +{ + QTestState *src, *dst; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *mig_path = NULL; + g_autofree char *uri = NULL; + g_autofree char *dst_args = NULL; + char marker[9]; + int fd; + + fd = g_file_open_tmp("qtest-ide-migration.XXXXXX", &mig_path, NULL); + g_assert(fd >= 0); + close(fd); + uri = g_strdup_printf("unix:%s", mig_path); + + src = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s,locking=off " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(src, &bmdma_bar, &ide_bar); + + ide_prepare_markers(src, dev, ide_bar); + ide_set_translation(dev, ide_bar, 8, 32); + ide_read_chs_marker(src, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + dst_args = g_strdup_printf( + "-machine pc " + "-blockdev driver=file,node-name=hda,filename=%s,locking=off " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming %s", + tmp_path[0], uri); + dst = qtest_init(dst_args); + + ide_migrate(src, dst, uri); + + /* Talk to the destination instead of the source */ + qpci_free_pc(pcibus); + pcibus = NULL; + free_pci_device(dev); + dev = get_pci_device(dst, &bmdma_bar, &ide_bar); + + ide_read_chs_marker(dst, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + free_pci_device(dev); + qtest_quit(dst); + ide_test_quit(src); + unlink(mig_path); +} + +/* A translation selected after the snapshot must not outlive loading it */ +static void test_migrate_chs_snapshot(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *img = NULL; + char marker[9]; + int fd; + + if (!have_qemu_img()) { + g_test_skip("QTEST_QEMU_IMG not set, snapshots need a qcow2 image"); + return; + } + + fd = g_file_open_tmp("qtest-ide-snapshot.XXXXXX", &img, NULL); + g_assert(fd >= 0); + close(fd); + g_assert(mkimg(img, "qcow2", TEST_IMAGE_SIZE / (1024 * 1024))); + + qts = ide_test_start( + "-blockdev driver=qcow2,node-name=hda,file.driver=file," + "file.filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", img); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + ide_prepare_markers(qts, dev, ide_bar); + + /* Snapshot taken while the default translation is in effect */ + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_DEFAULT); + ide_hmp_quiet(qts, "savevm s0"); + + ide_set_translation(dev, ide_bar, 8, 32); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + ide_hmp_quiet(qts, "loadvm s0"); + + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_DEFAULT); + + free_pci_device(dev); + ide_test_quit(qts); + unlink(img); +} + static void test_cdrom_pio(void) { cdrom_read_impl(1, CDROM_PIO); @@ -1327,6 +1555,9 @@ int main(int argc, char **argv) qtest_add_func("/ide/read_native", test_specify); qtest_add_func("/ide/specify/zero_sectors", test_specify_zero_sectors); + qtest_add_func("/ide/migration/chs_translation", + test_migrate_chs_translation); + qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot); qtest_add_func("/ide/identify", test_identify); From 5be90dfbc000a279b7cec668f9dbb131ce2b0c40 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 18:17:34 +0200 Subject: [PATCH 845/860] tests/qtest/ide-test: cover a rejected CHS translation in the stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_drive_post_load() refuses a logical CHS translation that no command could have selected, as the fields are a divisor in ide_set_sector() and a factor in ide_get_sector(). Nothing exercised that, a fixed QEMU having no way to produce such a stream. Migrate a guest that selected a translation to a file, replace the number of sectors per logical track in the subsection with a zero, and let a destination read the result back. The load has to fail rather than take the value, so the destination is asked not to exit on a failed incoming migration and its migration status is what the test looks at. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 81 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 19e9734d3c..4fa58ac525 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -1483,6 +1483,86 @@ static void test_migrate_chs_snapshot(void) unlink(img); } +/* A migration stream holds NUL bytes, so this cannot be a string search */ +static char *ide_stream_find(char *stream, gsize len, const char *name) +{ + gsize name_len = strlen(name); + gsize i; + + if (len < name_len) { + return NULL; + } + for (i = 0; i <= len - name_len; i++) { + if (memcmp(stream + i, name, name_len) == 0) { + return stream + i; + } + } + + return NULL; +} + +/* A translation no command could have selected has to be refused on load */ +static void test_migrate_chs_rejected(void) +{ + const char *name = "ide_drive/chs_translation"; + QTestState *src, *dst; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *path = NULL; + g_autofree char *uri = NULL; + g_autofree char *dst_args = NULL; + g_autofree char *stream = NULL; + char *subsection; + gsize len; + int fd; + + fd = g_file_open_tmp("qtest-ide-stream.XXXXXX", &path, NULL); + g_assert(fd >= 0); + close(fd); + uri = g_strdup_printf("file:%s", path); + + src = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(src, &bmdma_bar, &ide_bar); + + ide_set_translation(dev, ide_bar, 8, 32); + qtest_qmp_assert_success(src, "{ 'execute': 'migrate'," + " 'arguments': { 'uri': %s } }", uri); + qtest_qmp_eventwait(src, "STOP"); + ide_migration_wait(src, "completed"); + free_pci_device(dev); + ide_test_quit(src); + + /* + * Behind the name come version, heads and sectors, each big endian 32 bit. + * The name recurs in the description at the end of the stream, so the + * first match is the one carrying data. + */ + g_assert(g_file_get_contents(path, &stream, &len, NULL)); + subsection = ide_stream_find(stream, len, name); + g_assert(subsection); + g_assert_cmpint(subsection - stream + strlen(name) + 12, <=, len); + memset(subsection + strlen(name) + 8, 0, 4); + g_assert(g_file_set_contents(path, stream, len, NULL)); + + dst_args = g_strdup_printf( + "-machine pc " + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming defer", + tmp_path[0]); + dst = qtest_init(dst_args); + + qtest_qmp_assert_success(dst, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': %s," + " 'exit-on-error': false } }", uri); + ide_migration_wait(dst, "failed"); + + qtest_quit(dst); + unlink(path); +} + static void test_cdrom_pio(void) { cdrom_read_impl(1, CDROM_PIO); @@ -1558,6 +1638,7 @@ int main(int argc, char **argv) qtest_add_func("/ide/migration/chs_translation", test_migrate_chs_translation); qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot); + qtest_add_func("/ide/migration/chs_rejected", test_migrate_chs_rejected); qtest_add_func("/ide/identify", test_identify); From 07209133238f0d660ff33e33c32ce7dd95127adb Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 18:19:34 +0200 Subject: [PATCH 846/860] tests/qtest/ide-test: cover the IDENTIFY DEVICE geometry words MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit INITIALIZE DEVICE PARAMETERS has to leave the geometry the drive came with in words 3 and 6 and describe the translation it selected in words 54 to 58, and the data is cached, so which of the two a guest is told depends on when it first asked. Cover both orders, as each alone leaves half of it untested: one test has the data built while the default is in effect and then replaces the translation, which the cached copy has to follow, the other replaces it before the first IDENTIFY DEVICE, where the words describing the default have to keep doing so. Factor the reading of the data out of test_specify_zero_sectors() for the three of them to share. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 89 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 4fa58ac525..e7a56e0710 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -1196,6 +1196,18 @@ static void cdrom_read_impl(int nblocks, unsigned flags) free_pci_device(dev); } +static void ide_identify_words(QPCIDevice *dev, QPCIBar ide_bar, + uint16_t buf[256]) +{ + int i; + + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } +} + /* Zero sectors per track has to abort (ATA-5 8.16.6), not divide by zero */ static void test_specify_zero_sectors(void) { @@ -1221,11 +1233,7 @@ static void test_specify_zero_sectors(void) assert_bit_set(qpci_io_readb(dev, ide_bar, reg_error), ABRT); /* The refused request has to leave the default translation in effect */ - qpci_io_writeb(dev, ide_bar, reg_device, 0); - qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); - for (i = 0; i < 256; i++) { - buf[i] = qpci_io_readw(dev, ide_bar, reg_data); - } + ide_identify_words(dev, ide_bar, buf); g_assert_cmpint(buf[55], ==, 16); g_assert_cmpint(buf[56], ==, 63); @@ -1563,6 +1571,74 @@ static void test_migrate_chs_rejected(void) unlink(path); } +/* Words 54 to 58 follow the translation even when the data was cached first */ +static void test_specify_identify(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + unsigned int cyls; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* Have the data built while the default translation is still in effect */ + ide_identify_words(dev, ide_bar, buf); + cyls = buf[1]; + g_assert_cmpint(buf[3], ==, 16); + g_assert_cmpint(buf[6], ==, 63); + g_assert_cmpint(buf[53] & 1, ==, 1); + g_assert_cmpint(buf[55], ==, 16); + g_assert_cmpint(buf[56], ==, 63); + g_assert_cmpint(buf[57] | (buf[58] << 16), ==, cyls * 16 * 63); + + ide_set_translation(dev, ide_bar, 8, 32); + + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[1], ==, cyls); + g_assert_cmpint(buf[3], ==, 16); + g_assert_cmpint(buf[4], ==, 512 * 63); + g_assert_cmpint(buf[6], ==, 63); + g_assert_cmpint(buf[55], ==, 8); + g_assert_cmpint(buf[56], ==, 32); + g_assert_cmpint(buf[57] | (buf[58] << 16), ==, cyls * 8 * 32); + + free_pci_device(dev); + ide_test_quit(qts); +} + +/* Words 3 and 6 keep the drive's own geometry even if built after a change */ +static void test_specify_identify_default(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + /* No IDENTIFY DEVICE before this one, so nothing was cached yet */ + ide_set_translation(dev, ide_bar, 8, 32); + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[3], ==, 16); + g_assert_cmpint(buf[4], ==, 512 * 63); + g_assert_cmpint(buf[6], ==, 63); + g_assert_cmpint(buf[55], ==, 8); + g_assert_cmpint(buf[56], ==, 32); + g_assert_cmpint(buf[57] | (buf[58] << 16), ==, buf[1] * 8 * 32); + + free_pci_device(dev); + ide_test_quit(qts); +} + static void test_cdrom_pio(void) { cdrom_read_impl(1, CDROM_PIO); @@ -1635,6 +1711,9 @@ int main(int argc, char **argv) qtest_add_func("/ide/read_native", test_specify); qtest_add_func("/ide/specify/zero_sectors", test_specify_zero_sectors); + qtest_add_func("/ide/specify/identify", test_specify_identify); + qtest_add_func("/ide/specify/identify_default", + test_specify_identify_default); qtest_add_func("/ide/migration/chs_translation", test_migrate_chs_translation); qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot); From cafb3d96254fee4705d58cea2a8221498a361475 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 17:46:55 +0200 Subject: [PATCH 847/860] hw/ide: revert the CHS translation on a hardware reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A power on or hardware reset returns the device parameters to their power-on defaults (ATA-5 9.1). A software reset keeps them unless the guest asked with SET FEATURES 0xCC for the next reset to revert (ATA-5 9.2 and 8.16.6). ide_reset() applied the second rule to every reset, so a translation a guest selected outlived the reset of the machine it selected it on, and the guest that came up next addressed the disk through a geometry it never asked for. Neither ide_reset() nor, for AHCI, ide_bus_reset() could tell the two apart: a guest clearing SRST in the second host to device FIS of the software reset protocol lands in the same ahci_reset_port() as a COMRESET or a reset of the host adapter. Pass the kind down from the callers, which do know. ide_drive_pre_load() stays necessary: it restores the same fields, but a vmstate cannot depend on its device having been reset first. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command") Signed-off-by: Denis V. Lunev Reviewed-by: Philippe Mathieu-Daudé --- hw/ide/ahci.c | 12 ++++++------ hw/ide/cmd646.c | 2 +- hw/ide/core.c | 18 +++++++++--------- hw/ide/ide-internal.h | 7 ++++++- hw/ide/isa.c | 2 +- hw/ide/macio.c | 2 +- hw/ide/mmio.c | 2 +- hw/ide/piix.c | 2 +- hw/ide/sii3112.c | 6 +++--- hw/ide/via.c | 2 +- 10 files changed, 30 insertions(+), 25 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 749f0efa1d..49f3047e6f 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -37,7 +37,7 @@ static void check_cmd(AHCIState *s, int port); static void handle_cmd(AHCIState *s, int port, uint8_t slot); -static void ahci_reset_port(AHCIState *s, int port); +static void ahci_reset_port(AHCIState *s, int port, IDEResetKind kind); static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i); static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot); static void ahci_init_d2h(AHCIDevice *ad); @@ -334,7 +334,7 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) case AHCI_PORT_REG_SCR_CTL: if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && ((val & AHCI_SCR_SCTL_DET) == 0)) { - ahci_reset_port(s, port); + ahci_reset_port(s, port, IDE_RESET_HARDWARE); } pr->scr_ctl = val; break; @@ -619,7 +619,7 @@ static void ahci_set_signature(AHCIDevice *ad, uint32_t sig) s->lcyl, s->hcyl, sig); } -static void ahci_reset_port(AHCIState *s, int port) +static void ahci_reset_port(AHCIState *s, int port, IDEResetKind kind) { AHCIDevice *d = &s->dev[port]; AHCIPortRegs *pr = &d->port_regs; @@ -628,7 +628,7 @@ static void ahci_reset_port(AHCIState *s, int port) trace_ahci_reset_port(s, port); - ide_bus_reset(&d->port); + ide_bus_reset(&d->port, kind); ide_state->ncq_queues = AHCI_MAX_CMDS; pr->scr_stat = 0; @@ -1244,7 +1244,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, * COMRESET or by setting and clearing the SRST bit. Therefore, * the logic for this is found in ahci_init_d2h() and not here. */ - ahci_reset_port(s, port); + ahci_reset_port(s, port, IDE_RESET_SOFTWARE); } break; } @@ -1650,7 +1650,7 @@ void ahci_reset(AHCIState *s) pr->irq_mask = 0; pr->scr_ctl = 0; pr->cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON; - ahci_reset_port(s, i); + ahci_reset_port(s, i, IDE_RESET_HARDWARE); } } diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index d44a90a162..79ab65d64a 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -214,7 +214,7 @@ static void cmd646_reset(DeviceState *dev) unsigned int i; for (i = 0; i < 2; i++) { - ide_bus_reset(&d->bus[i]); + ide_bus_reset(&d->bus[i], IDE_RESET_HARDWARE); } } diff --git a/hw/ide/core.c b/hw/ide/core.c index 5a5f072630..3716cf1b5d 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1347,7 +1347,7 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) } } -static void ide_reset(IDEState *s) +static void ide_reset(IDEState *s, IDEResetKind kind) { trace_ide_reset(s); @@ -1356,7 +1356,7 @@ static void ide_reset(IDEState *s) s->pio_aiocb = NULL; } - if (s->reset_reverts) { + if (kind == IDE_RESET_HARDWARE || s->reset_reverts) { s->reset_reverts = false; s->heads = s->drive_heads; s->sectors = s->drive_sectors; @@ -1424,7 +1424,7 @@ static bool cmd_device_reset(IDEState *s, uint8_t cmd) ide_cancel_dma_sync(s); /* Reset any PIO commands, reset signature, etc */ - ide_reset(s); + ide_reset(s, IDE_RESET_SOFTWARE); /* RESET: ATA8-ACS3 7.10.4 "Normal Outputs"; * ATA8-ACS3 Table 184 "Device Signatures for Normal Output" */ @@ -2348,7 +2348,7 @@ static void ide_perform_srst(IDEState *s) ide_cancel_dma_sync(s); /* Cancel PIO callback, reset registers/signature, etc */ - ide_reset(s); + ide_reset(s, IDE_RESET_SOFTWARE); /* perform diagnostic */ cmd_exec_dev_diagnostic(s, WIN_DIAGNOSE); @@ -2553,7 +2553,7 @@ static void ide_dummy_transfer_stop(IDEState *s) s->io_buffer[3] = 0xff; } -void ide_bus_reset(IDEBus *bus) +void ide_bus_reset(IDEBus *bus, IDEResetKind kind) { /* pending async DMA - needs the IDEState before it is reset */ if (bus->dma->aiocb) { @@ -2564,8 +2564,8 @@ void ide_bus_reset(IDEBus *bus) bus->unit = 0; bus->cmd = 0; - ide_reset(&bus->ifs[0]); - ide_reset(&bus->ifs[1]); + ide_reset(&bus->ifs[0], kind); + ide_reset(&bus->ifs[1], kind); ide_clear_hob(bus); /* reset dma provider too */ @@ -2679,7 +2679,7 @@ int ide_init_drive(IDEState *s, IDEDevice *dev, IDEDriveKind kind, Error **errp) pstrcpy(s->version, sizeof(s->version), QEMU_HW_VERSION); } - ide_reset(s); + ide_reset(s, IDE_RESET_HARDWARE); blk_iostatus_enable(s->blk); return 0; } @@ -2816,7 +2816,7 @@ void ide_bus_init_output_irq(IDEBus *bus, qemu_irq irq_out) for(i = 0; i < 2; i++) { ide_init1(bus, i); - ide_reset(&bus->ifs[i]); + ide_reset(&bus->ifs[i], IDE_RESET_HARDWARE); } bus->irq = irq_out; bus->dma = &ide_dma_nop; diff --git a/hw/ide/ide-internal.h b/hw/ide/ide-internal.h index 281d07c9d5..094772209d 100644 --- a/hw/ide/ide-internal.h +++ b/hw/ide/ide-internal.h @@ -393,7 +393,12 @@ extern const VMStateDescription vmstate_ide_drive; #define VMSTATE_IDE_DRIVE(_field, _state) \ VMSTATE_STRUCT(_field, _state, 1, vmstate_ide_drive, IDEState) -void ide_bus_reset(IDEBus *bus); +typedef enum { + IDE_RESET_HARDWARE, /* power on, hardware reset or COMRESET, ATA-5 9.1 */ + IDE_RESET_SOFTWARE, /* SRST or DEVICE RESET, ATA-5 9.2 */ +} IDEResetKind; + +void ide_bus_reset(IDEBus *bus, IDEResetKind kind); int64_t ide_get_sector(IDEState *s); void ide_set_sector(IDEState *s, int64_t sector_num); diff --git a/hw/ide/isa.c b/hw/ide/isa.c index c97b7a1ff4..30f02867b1 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -51,7 +51,7 @@ static void isa_ide_reset(DeviceState *d) { ISAIDEState *s = ISA_IDE(d); - ide_bus_reset(&s->bus); + ide_bus_reset(&s->bus, IDE_RESET_HARDWARE); } static const VMStateDescription vmstate_ide_isa = { diff --git a/hw/ide/macio.c b/hw/ide/macio.c index a7ed41fa26..40fb4f3b4f 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -368,7 +368,7 @@ static void macio_ide_reset(DeviceState *dev) { MACIOIDEState *d = MACIO_IDE(dev); - ide_bus_reset(&d->bus); + ide_bus_reset(&d->bus, IDE_RESET_HARDWARE); } static int ide_nop_int(const IDEDMA *dma, bool is_write) diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 0de904ac56..00819d559d 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -55,7 +55,7 @@ static void mmio_ide_reset(DeviceState *dev) { MMIOIDEState *s = MMIO_IDE(dev); - ide_bus_reset(&s->bus); + ide_bus_reset(&s->bus, IDE_RESET_HARDWARE); } static uint64_t mmio_ide_read(void *opaque, hwaddr addr, diff --git a/hw/ide/piix.c b/hw/ide/piix.c index a0f2709c69..a8472f3e58 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -111,7 +111,7 @@ static void piix_ide_reset(DeviceState *dev) int i; for (i = 0; i < 2; i++) { - ide_bus_reset(&d->bus[i]); + ide_bus_reset(&d->bus[i], IDE_RESET_HARDWARE); } /* PCI command register default value (0000h) per [1, p.48]. */ diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 9b28c691fd..03313eb271 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -185,7 +185,7 @@ static void sii3112_reg_write(void *opaque, hwaddr addr, case 0x100: d->regs[0].scontrol = val & 0xfff; if (val & 1) { - ide_bus_reset(&d->i.bus[0]); + ide_bus_reset(&d->i.bus[0], IDE_RESET_HARDWARE); } break; case 0x148: @@ -194,7 +194,7 @@ static void sii3112_reg_write(void *opaque, hwaddr addr, case 0x180: d->regs[1].scontrol = val & 0xfff; if (val & 1) { - ide_bus_reset(&d->i.bus[1]); + ide_bus_reset(&d->i.bus[1], IDE_RESET_HARDWARE); } break; case 0x1c8: @@ -243,7 +243,7 @@ static void sii3112_reset(DeviceState *dev) for (i = 0; i < 2; i++) { s->regs[i].confstat = 0x6515 << 16; - ide_bus_reset(&s->i.bus[i]); + ide_bus_reset(&s->i.bus[i], IDE_RESET_HARDWARE); } } diff --git a/hw/ide/via.c b/hw/ide/via.c index 3a77d744ca..6c0610ff41 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -127,7 +127,7 @@ static void via_ide_reset(DeviceState *dev) int i; for (i = 0; i < ARRAY_SIZE(d->bus); i++) { - ide_bus_reset(&d->bus[i]); + ide_bus_reset(&d->bus[i], IDE_RESET_HARDWARE); } pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy mode */ From d1634ae78dd4910a37f11c07d313adb7e1bf5c97 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 18:20:23 +0200 Subject: [PATCH 848/860] tests/qtest/ide-test: cover the CHS translation across resets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A translation the guest selected has to survive a software reset and not a hardware one, and the two arrive at the same ide_reset(), so a fix for either direction can break the other. Select a translation, put the drive through a software reset and then through a machine reset, and name the sector each translation picks along the way. The marker read says which translation the device is addressing the disk with, while IDENTIFY DEVICE words 55 and 56 say which one it reports. The machine reset leaves the PCI command register cleared, so the device has to be enabled again before the ports answer. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index e7a56e0710..f14a0851f0 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -1639,6 +1639,51 @@ static void test_specify_identify_default(void) ide_test_quit(qts); } +/* A hardware reset reverts the translation (ATA-5 9.1), SRST does not (9.2) */ +static void test_specify_reset(void) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar, ide_bar2; + uint16_t buf[256]; + char marker[9]; + + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + ide_bar2 = qpci_legacy_iomap(dev, IDE_BASE2); + + ide_prepare_markers(qts, dev, ide_bar); + ide_set_translation(dev, ide_bar, 8, 32); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + qpci_io_writeb(dev, ide_bar2, 0, IDE_CTRL_RESET); + qpci_io_writeb(dev, ide_bar2, 0, 0); + ide_wait_clear(qts, BSY); + + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[55], ==, 8); + g_assert_cmpint(buf[56], ==, 32); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_CUSTOM); + + qtest_qmp_assert_success(qts, "{ 'execute': 'system_reset' }"); + qtest_qmp_eventwait(qts, "RESET"); + qpci_device_enable(dev); + + ide_identify_words(dev, ide_bar, buf); + g_assert_cmpint(buf[55], ==, 16); + g_assert_cmpint(buf[56], ==, 63); + ide_read_chs_marker(qts, dev, ide_bar, 0, 1, 1, marker); + g_assert_cmpstr(marker, ==, CHS_MARKER_DEFAULT); + + free_pci_device(dev); + ide_test_quit(qts); +} + static void test_cdrom_pio(void) { cdrom_read_impl(1, CDROM_PIO); @@ -1714,6 +1759,7 @@ int main(int argc, char **argv) qtest_add_func("/ide/specify/identify", test_specify_identify); qtest_add_func("/ide/specify/identify_default", test_specify_identify_default); + qtest_add_func("/ide/specify/reset", test_specify_reset); qtest_add_func("/ide/migration/chs_translation", test_migrate_chs_translation); qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot); From e8a3b42ae54e688360c8ac4e6d9f7c4f28c1411f Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 14 Aug 2026 18:07:36 +0200 Subject: [PATCH 849/860] hw/ide: drop a redundant interrupt from INITIALIZE DEVICE PARAMETERS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_bus_exec_cmd() raises the interrupt for every command handler that reports the command complete, which cmd_specify() does, so the request it raised itself was the first of two. The one from ide_bus_exec_cmd() is the one that belongs there, being raised after BSY is cleared and after ide_cmd_done() has let the bus master post its own completion. Cc: John Snow Cc: Peter Maydell Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev Reviewed-by: Philippe Mathieu-Daudé --- hw/ide/core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 3716cf1b5d..8190549ee8 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1675,7 +1675,6 @@ static bool cmd_specify(IDEState *s, uint8_t cmd) if (s->identify_set) { ide_identify_chs(s); } - ide_bus_set_irq(s->bus); return true; } From 1332990194b53f5ca6a4fe7ac93cf4ad6eecfa9c Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 17 Aug 2026 23:44:59 +0200 Subject: [PATCH 850/860] hw/ide: reject an out-of-range PIO transfer window on load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ide_drive_pio_post_load() validates end_transfer_fn_idx but takes cur_io_buffer_offset and cur_io_buffer_len straight from the migration stream, so data_ptr and data_end can be placed anywhere within +-2GB of the 131076-byte io_buffer allocation. Both fields are signed 32-bit. The subsection loader consumes every subsection present in the stream without consulting needed(), so a crafted stream can inject ide_drive/pio_state for a drive that was never in a DRQ state. Once data_end is out of bounds, ide_data_writew() only compares the guest's pointer against that same bogus data_end, and the resumed guest turns a repeated outw to the data port into a controlled 16-bit heap write. end_transfer_fn_idx picks the direction, so the read side of the same code path leaks host heap instead. Validate the window against io_buffer_total_len and fail the load. The subtraction form avoids overflowing the addition. Reported-by: XlabAI Team of Tencent Xuanwu Lab Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4179 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3738 Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index 8190549ee8..0dca2b5c52 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2898,6 +2898,12 @@ static int ide_drive_pio_post_load(void *opaque, int version_id) if (s->end_transfer_fn_idx >= ARRAY_SIZE(transfer_end_table)) { return -EINVAL; } + if (s->cur_io_buffer_offset < 0 || s->cur_io_buffer_len < 0 || + s->cur_io_buffer_offset > s->io_buffer_total_len || + s->cur_io_buffer_len > + s->io_buffer_total_len - s->cur_io_buffer_offset) { + return -EINVAL; + } s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx]; s->data_ptr = s->io_buffer + s->cur_io_buffer_offset; s->data_end = s->data_ptr + s->cur_io_buffer_len; From e46245cd3ba0b7a558fd33dc24dd630a0a9d59d0 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 18 Aug 2026 01:05:36 +0200 Subject: [PATCH 851/860] tests/qtest/ide-test: cover the migrated PIO transfer window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /ide/migration/pio_state_rejected leaves a drive in DRQ so the source streams ide_drive/pio_state, rewrites cur_io_buffer_offset to the end of the io_buffer, and expects the destination to refuse the load. It asserts the window the source wrote before overwriting it, so a wrong guess at the stream layout fails the test rather than passing it for the wrong reason. Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 79 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index f14a0851f0..a3109da908 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -1571,6 +1571,83 @@ static void test_migrate_chs_rejected(void) unlink(path); } +/* A PIO transfer window reaching past the io_buffer has to be refused */ +static void test_migrate_pio_state_rejected(void) +{ + const char *name = "ide_drive/pio_state"; + /* IDE_DMA_BUF_SECTORS * 512 + 4, the length of the streamed io_buffer */ + const gsize io_buffer_len = 256 * 512 + 4; + /* cur_io_buffer_offset and cur_io_buffer_len, big endian */ + const uint8_t in_bounds[8] = { 0, 0, 0, 0, 0, 0, 0x02, 0 }; + const uint8_t past_the_end[8] = { 0, 0x02, 0, 0x04, 0, 0, 0x10, 0 }; + QTestState *src, *dst; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + g_autofree char *path = NULL; + g_autofree char *uri = NULL; + g_autofree char *dst_args = NULL; + g_autofree char *stream = NULL; + char *window; + gsize len; + int fd; + + fd = g_file_open_tmp("qtest-ide-stream.XXXXXX", &path, NULL); + g_assert(fd >= 0); + close(fd); + uri = g_strdup_printf("file:%s", path); + + src = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + dev = get_pci_device(src, &bmdma_bar, &ide_bar); + + /* WRITE SECTOR(S) waits in DRQ for the data, so pio_state is streamed */ + qpci_io_writeb(dev, ide_bar, reg_nsectors, 1); + qpci_io_writeb(dev, ide_bar, reg_lba_low, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_high, 0); + qpci_io_writeb(dev, ide_bar, reg_device, LBA); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_WRITE); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRQ); + + qtest_qmp_assert_success(src, "{ 'execute': 'migrate'," + " 'arguments': { 'uri': %s } }", uri); + qtest_qmp_eventwait(src, "STOP"); + ide_migration_wait(src, "completed"); + free_pci_device(dev); + ide_test_quit(src); + + /* + * Behind the name come the version and req_nb_sectors as big endian 32 + * bit, then the io_buffer array, then the transfer window this rewrites. + * Asserting the window the source streamed keeps that arithmetic honest. + */ + g_assert(g_file_get_contents(path, &stream, &len, NULL)); + window = ide_stream_find(stream, len, name); + g_assert(window); + window += strlen(name) + 8 + io_buffer_len; + g_assert_cmpint(window - stream + sizeof(past_the_end), <=, len); + g_assert_cmpint(memcmp(window, in_bounds, sizeof(in_bounds)), ==, 0); + memcpy(window, past_the_end, sizeof(past_the_end)); + g_assert(g_file_set_contents(path, stream, len, NULL)); + + dst_args = g_strdup_printf( + "-machine pc " + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming defer", + tmp_path[0]); + dst = qtest_init(dst_args); + + qtest_qmp_assert_success(dst, "{ 'execute': 'migrate-incoming'," + " 'arguments': { 'uri': %s," + " 'exit-on-error': false } }", uri); + ide_migration_wait(dst, "failed"); + + qtest_quit(dst); + unlink(path); +} + /* Words 54 to 58 follow the translation even when the data was cached first */ static void test_specify_identify(void) { @@ -1764,6 +1841,8 @@ int main(int argc, char **argv) test_migrate_chs_translation); qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot); qtest_add_func("/ide/migration/chs_rejected", test_migrate_chs_rejected); + qtest_add_func("/ide/migration/pio_state_rejected", + test_migrate_pio_state_rejected); qtest_add_func("/ide/identify", test_identify); From d7f16bad8f0263ca01c1da61d5d69ee79fca837f Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 17 Aug 2026 23:44:59 +0200 Subject: [PATCH 852/860] hw/ide/ahci: refuse a PIO transfer with no command header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ahci_map_clb_address() already clears cur_cmd, so every consumer of it has to cope with there being no current command. ahci_pio_transfer(), ahci_commit_buf() and ahci_populate_sglist() all dereference it unconditionally instead. Give the three of them a NULL check. Declaring the data transferred anyway is not enough: ide_transfer_start() goes on to call the end transfer function, and for a multi-sector write that is ide_sector_write(), which commits an io_buffer the guest never refilled. Clearing PxCMD.ST during a WRITE SECTOR(S) of two sectors therefore writes the first sector's contents over the second, at a sector the guest chose. Let pio_transfer report that nothing was transferred and halt there, so no callback acts on a buffer that was never filled. Only the AHCI HBA implements the callback, so the signature change is local to it. Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/ahci.c | 47 +++++++++++++++++++++++++++++++--------- hw/ide/core.c | 11 +++++++++- hw/ide/trace-events | 2 ++ include/hw/ide/ide-dma.h | 3 ++- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 49f3047e6f..995b40efd5 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -906,12 +906,12 @@ static int prdt_tbl_entry_size(const AHCI_SG *tbl) static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, AHCICmdHdr *cmd, int64_t limit, uint64_t offset) { - uint16_t opts = le16_to_cpu(cmd->opts); - uint16_t prdtl = le16_to_cpu(cmd->prdtl); - uint64_t cfis_addr = le64_to_cpu(cmd->tbl_addr); - uint64_t prdt_addr = cfis_addr + 0x80; - dma_addr_t prdt_len = (prdtl * sizeof(AHCI_SG)); - dma_addr_t real_prdt_len = prdt_len; + uint16_t opts; + uint16_t prdtl; + uint64_t cfis_addr; + uint64_t prdt_addr; + dma_addr_t prdt_len; + dma_addr_t real_prdt_len; uint8_t *prdt; int i; int r = 0; @@ -923,6 +923,18 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, trace_ahci_populate_sglist(ad->hba, ad->port_no); + if (!cmd) { + trace_ahci_populate_sglist_no_cmd(ad->hba, ad->port_no); + return -1; + } + + opts = le16_to_cpu(cmd->opts); + prdtl = le16_to_cpu(cmd->prdtl); + cfis_addr = le64_to_cpu(cmd->tbl_addr); + prdt_addr = cfis_addr + 0x80; + prdt_len = (prdtl * sizeof(AHCI_SG)); + real_prdt_len = prdt_len; + if (!prdtl) { trace_ahci_populate_sglist_no_prdtl(ad->hba, ad->port_no, opts); return -1; @@ -1371,18 +1383,27 @@ out: } /* Transfer PIO data between RAM and device */ -static void ahci_pio_transfer(const IDEDMA *dma) +static bool ahci_pio_transfer(const IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; uint32_t size = (uint32_t)(s->data_end - s->data_ptr); /* write == ram -> device */ - uint16_t opts = le16_to_cpu(ad->cur_cmd->opts); - int is_write = opts & AHCI_CMD_WRITE; - int is_atapi = opts & AHCI_CMD_ATAPI; + uint16_t opts; + int is_write; + int is_atapi; int has_sglist = 0; bool pio_fis_i; + if (ad->cur_cmd == NULL) { + trace_ahci_pio_transfer_no_cmd(ad->hba, ad->port_no); + return false; + } + + opts = le16_to_cpu(ad->cur_cmd->opts); + is_write = opts & AHCI_CMD_WRITE; + is_atapi = opts & AHCI_CMD_ATAPI; + /* The PIO Setup FIS is received prior to transfer, but the interrupt * is only triggered after data is received. * @@ -1430,6 +1451,8 @@ out: if (pio_fis_i) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS); } + + return true; } static void ahci_start_dma(const IDEDMA *dma, IDEState *s, @@ -1492,6 +1515,10 @@ static void ahci_commit_buf(const IDEDMA *dma, uint32_t tx_bytes) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + if (ad->cur_cmd == NULL) { + return; + } + tx_bytes += le32_to_cpu(ad->cur_cmd->status); ad->cur_cmd->status = cpu_to_le32(tx_bytes); } diff --git a/hw/ide/core.c b/hw/ide/core.c index 0dca2b5c52..06c18dbf09 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -80,6 +80,7 @@ static const char *IDE_DMA_CMD_str(enum ide_dma_cmd enval) } static void ide_dummy_transfer_stop(IDEState *s); +static void ide_transfer_halt(IDEState *s); const MemoryRegionPortio ide_portio_list[] = { { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, @@ -568,7 +569,15 @@ bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, s->end_transfer_func = end_transfer_func; return false; } - s->bus->dma->ops->pio_transfer(s->bus->dma); + if (!s->bus->dma->ops->pio_transfer(s->bus->dma)) { + /* + * No data reached the buffer, so the caller must not act on it. A + * write would otherwise commit whatever the previous phase left + * there to the next sector. + */ + ide_transfer_halt(s); + return false; + } return true; } diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 57042cafdd..f1472f5852 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -85,6 +85,7 @@ ahci_reset_port(void *s, int port) "ahci(%p)[%d]: reset port" ahci_unmap_fis_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL FIS address" ahci_unmap_clb_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL CLB address" ahci_populate_sglist(void *s, int port) "ahci(%p)[%d]" +ahci_populate_sglist_no_cmd(void *s, int port) "ahci(%p)[%d]: no command header" ahci_populate_sglist_no_prdtl(void *s, int port, uint16_t opts) "ahci(%p)[%d]: no sg list given by guest: 0x%04x" ahci_populate_sglist_no_map(void *s, int port) "ahci(%p)[%d]: DMA mapping failed" ahci_populate_sglist_short_map(void *s, int port) "ahci(%p)[%d]: mapped less than expected" @@ -109,6 +110,7 @@ handle_cmd_badfis(void *s, int port) "ahci(%p)[%d]: guest provided an invalid cm handle_cmd_badmap(void *s, int port, uint64_t len) "ahci(%p)[%d]: dma_memory_map failed, 0x%02"PRIx64" != 0x80" handle_cmd_unhandled_fis(void *s, int port, uint8_t b0, uint8_t b1, uint8_t b2) "ahci(%p)[%d]: unhandled FIS type. cmd_fis: 0x%02x-%02x-%02x" ahci_pio_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist" +ahci_pio_transfer_no_cmd(void *s, int port) "ahci(%p)[%d]: PIO transfer without a command header" ahci_start_dma(void *s, int port) "ahci(%p)[%d]: start dma" ahci_dma_prepare_buf(void *s, int port, int32_t io_buffer_size, int32_t limit) "ahci(%p)[%d]: prepare buf limit=%"PRId32" prepared=%"PRId32 ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population failed" diff --git a/include/hw/ide/ide-dma.h b/include/hw/ide/ide-dma.h index 296010a4e0..34154b7cbc 100644 --- a/include/hw/ide/ide-dma.h +++ b/include/hw/ide/ide-dma.h @@ -10,6 +10,7 @@ typedef struct IDEDMA IDEDMA; typedef void DMAStartFunc(const IDEDMA *, IDEState *, BlockCompletionFunc *); typedef void DMAVoidFunc(const IDEDMA *); +typedef bool DMABoolFunc(const IDEDMA *); typedef int DMAIntFunc(const IDEDMA *, bool); typedef int32_t DMAInt32Func(const IDEDMA *, int32_t len); typedef void DMAu32Func(const IDEDMA *, uint32_t); @@ -17,7 +18,7 @@ typedef void DMAStopFunc(const IDEDMA *, bool); struct IDEDMAOps { DMAStartFunc *start_dma; - DMAVoidFunc *pio_transfer; + DMABoolFunc *pio_transfer; DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; From a2fbf1785b7a464d61c0f60c4ad0a86d82925e5d Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 17 Aug 2026 23:44:59 +0200 Subject: [PATCH 853/860] hw/ide/ahci: clear cur_cmd when the command list is unmapped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ahci_unmap_clb_address() drops the CLB mapping but leaves cur_cmd pointing into it. The cancel added by commit d9f78431d8 covers the buffered reads, and ide_cancel_dma_sync() drains bus->dma->aiocb, but neither reaches IDEState::pio_aiocb: a PIO write started before the guest cleared PxCMD.ST completes afterwards and runs its second DRQ phase against the stale header. That is harmless while the CLB is direct RAM, because unmapping it changes nothing. It is a use-after-free once PxCLB points at an MMIO region, where address_space_map() hands out a bounce buffer that dma_memory_unmap() then frees. Clear cur_cmd after the cancel, so nothing reachable from a later completion still refers to the freed mapping. Reported-by: Katherine Leaver Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3719 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4043 Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/ahci.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 995b40efd5..4c138b0c51 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -743,6 +743,12 @@ static void ahci_unmap_clb_address(AHCIDevice *ad) /* Cancel in-flight reads that would complete against a cleared cur_cmd. */ ide_cancel_dma_sync(ide_bus_active_if(&ad->port)); + /* + * Whatever survives the cancel must not be left pointing into the + * mapping this function is about to drop. + */ + ad->cur_cmd = NULL; + if (ad->lst == NULL) { trace_ahci_unmap_clb_address_null(ad->hba, ad->port_no); return; From d05ae87e7a6ab519a99f8c44f40d97152adb8b08 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 17 Aug 2026 23:44:59 +0200 Subject: [PATCH 854/860] tests/qtest/ahci: regression test for a PIO write vs. engine stop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /ahci/io/pio/engine_stop: hold the backend write of a two-sector PIO write with a blkdebug breakpoint, clear PxCMD.ST so the command list is unmapped underneath it, then let the write complete. The second DRQ phase runs from that completion and reaches ahci_pio_transfer() with no command header. Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ahci-test.c | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 30d7005626..84d4e6b0a5 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -1793,6 +1793,76 @@ static void test_atapi_engine_restart_dma(void) test_atapi_engine_restart_in_flight(true); } +/* + * Regression test: a PIO write outlives the command list it was issued from. + * ide_cancel_dma_sync() does not reach s->pio_aiocb, so the second DRQ phase + * runs from the write completion after PxCLB has been unmapped and must not + * touch the command header any more. + */ +static void test_write_engine_stop_in_flight(void) +{ + AHCIQState *ahci; + AHCICommand *cmd; + unsigned char *tx; + unsigned char *rx; + uint64_t ptr; + uint8_t port; + size_t bufsize = AHCI_SECTOR_SIZE * 2; + size_t i; + + ahci = ahci_boot_and_enable("-drive file=blkdebug::%s,if=none,id=drive0," + "format=%s,cache=writeback " + "-M q35 " + "-device ide-hd,drive=drive0 ", + tmp_path, imgfmt); + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + tx = g_malloc(bufsize); + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + qtest_memwrite(ahci->parent->qts, ptr, tx, bufsize); + + /* Zero the second sector, which the abandoned command must not reach. */ + rx = g_malloc0(AHCI_SECTOR_SIZE); + ahci_io(ahci, port, CMD_WRITE_DMA, rx, AHCI_SECTOR_SIZE, 1); + + /* Suspend the backend write so the first sector stays in flight. */ + g_free(qtest_hmp(ahci->parent->qts, + "qemu-io drive0 \"break write_aio wr\"")); + + cmd = ahci_command_create(CMD_WRITE_PIO); + ahci_command_adjust(cmd, 0, ptr, bufsize, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Drop the command list while the write is still outstanding. */ + ahci_px_clr(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + + g_free(qtest_hmp(ahci->parent->qts, "qemu-io drive0 \"resume wr\"")); + + /* Round-trip through the device to confirm qemu is still alive. */ + ahci_px_rreg(ahci, port, AHCI_PX_TFD); + + /* + * The second DRQ phase never fetched its data, so the sector it would + * have carried has to be untouched rather than hold a copy of the first. + */ + ahci_px_set(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST); + memset(rx, 0xff, AHCI_SECTOR_SIZE); + ahci_io(ahci, port, CMD_READ_DMA, rx, AHCI_SECTOR_SIZE, 1); + for (i = 0; i < AHCI_SECTOR_SIZE; i++) { + g_assert_cmpint(rx[i], ==, 0); + } + + ahci_command_free(cmd); + ahci_free(ahci, ptr); + g_free(rx); + g_free(tx); + ahci_shutdown(ahci); +} + /* * Regression test: a multi-sector ATAPI read fetches its later sectors from * inside the first read's completion; a concurrent drain (as a guest reset @@ -2281,6 +2351,8 @@ int main(int argc, char **argv) test_atapi_engine_restart_pio); qtest_add_func("/ahci/cdrom/engine_restart/dma", test_atapi_engine_restart_dma); + qtest_add_func("/ahci/io/pio/engine_stop", + test_write_engine_stop_in_flight); qtest_add_func("/ahci/cdrom/drain/pio", test_atapi_drain_pio); qtest_add_func("/ahci/cdrom/drain/dma", test_atapi_drain_dma); From 2713717148f538577e5f2a5d4d9dc272f7a8dc4b Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 17 Aug 2026 23:44:59 +0200 Subject: [PATCH 855/860] hw/ide/ahci: treat a failed PRDT walk as a PIO transfer failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ahci_dma_prepare_buf() returns -1 when it cannot build a scatter-gather list, the PRDTL of zero case among them. ahci_pio_transfer() tests the result for truth, so a failure sets has_sglist and the transfer goes ahead against whatever s->sg holds. AHCI 1.3.1 is explicit about the zero case: "If this field is '0', then no data transfer shall occur with the command." Test for a positive byte count instead. A successful walk that yields nothing to transfer is already handled by the size check below. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4043 Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/ahci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 4c138b0c51..436a0eaab6 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1428,7 +1428,7 @@ static bool ahci_pio_transfer(const IDEDMA *dma) goto out; } - if (ahci_dma_prepare_buf(dma, size)) { + if (ahci_dma_prepare_buf(dma, size) > 0) { has_sglist = 1; } From 795af987ce4cbac260582189cff2394a27eff7cb Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 17 Aug 2026 23:44:59 +0200 Subject: [PATCH 856/860] hw/ide/ahci: reject a command header with an invalid FIS length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AHCI 1.3.1 defines CFL in the command header as the "Length of the Command FIS", where "A length of '0' or '1' is illegal" and "The maximum value allowed is 10h, or 16 DW". handle_cmd() never looks at it, so an all-zero command header is executable: its zero tbl_addr maps a command table at guest physical address 0, and a guest that has put a valid Register H2D FIS there gets it run. That is the reachability a guest gains by pointing PxCLB at an MMIO region, where the CLB is a zero-filled bounce buffer rather than anything the guest wrote. Reject a header whose CFL falls outside the legal range. Nothing else consults it; the command FIS is always mapped at its full 128 bytes. The slot is dropped without reporting anything, as the unmappable command table beside it already is. No PxIS bit describes a malformed command header: HBFS is for a host bus error, "such as a bad software pointer", which is why the short mapping below raises it and this does not. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4043 Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/ahci.c | 9 +++++++++ hw/ide/trace-events | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 436a0eaab6..2b2ef873e0 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1334,6 +1334,7 @@ static void handle_cmd(AHCIState *s, int port, uint8_t slot) AHCICmdHdr *cmd; uint8_t *cmd_fis; dma_addr_t cmd_len; + uint8_t cfl; if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { /* Engine currently busy, try again later */ @@ -1346,6 +1347,14 @@ static void handle_cmd(AHCIState *s, int port, uint8_t slot) return; } cmd = get_cmd_header(s, port, slot); + + /* AHCI 1.3.1: a CFL below 2 dwords or above 16 is illegal */ + cfl = le16_to_cpu(cmd->opts) & AHCI_CMD_HDR_CMD_FIS_LEN; + if (cfl < 2 || cfl > 16) { + trace_handle_cmd_badcfl(s, port, le16_to_cpu(cmd->opts)); + return; + } + /* remember current slot handle for later */ s->dev[port].cur_cmd = cmd; diff --git a/hw/ide/trace-events b/hw/ide/trace-events index f1472f5852..3ab5e7bd1d 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -106,6 +106,7 @@ handle_reg_h2d_fis_res(void *s, int port, char b0, char b1, char b2) "ahci(%p)[% handle_cmd_busy(void *s, int port) "ahci(%p)[%d]: engine busy" handle_cmd_nolist(void *s, int port) "ahci(%p)[%d]: handle_cmd called without s->dev[port].lst" handle_cmd_badport(void *s, int port) "ahci(%p)[%d]: guest accessed unused port" +handle_cmd_badcfl(void *s, int port, uint16_t opts) "ahci(%p)[%d]: guest provided an invalid cmd FIS length: 0x%04x" handle_cmd_badfis(void *s, int port) "ahci(%p)[%d]: guest provided an invalid cmd FIS" handle_cmd_badmap(void *s, int port, uint64_t len) "ahci(%p)[%d]: dma_memory_map failed, 0x%02"PRIx64" != 0x80" handle_cmd_unhandled_fis(void *s, int port, uint8_t b0, uint8_t b1, uint8_t b2) "ahci(%p)[%d]: unhandled FIS type. cmd_fis: 0x%02x-%02x-%02x" From 5bdc12fed592a1673f64ca1e8243046632f83a26 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 18 Aug 2026 09:55:48 +0200 Subject: [PATCH 857/860] hw/ide/ahci: drain the ports on teardown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ahci_uninit() frees s->dev without touching the requests still in flight. The only blk_aio_cancel() for them lives in ahci_reset_port(), which the unplug path does not run, and the ide-hd child's own drain is deferred through call_rcu so it happens after the free. A guest that powers the root port slot off through SLTCTL, or writes the ACPI ejection register, while a read is outstanding therefore leaves the completion to run against freed memory. A plain device_del is not affected: the pciehp attention-button flow resets the secondary bus first, which cancels through the reset path. Surprise removal is what skips it. Cancelling the NCQ requests alone is not enough. IDEDMA and IDEBus are embedded in AHCIDevice, so a plain DMA read reaches the freed array through dma_blk_cb() and a PIO read through ide_buffered_readv_cb(), neither of which the NCQ bookkeeping covers. ide_exit() drains nothing and frees io_buffer, which an outstanding request may still target. Move the NCQ cancel loop into a helper, run it from ahci_uninit() too, and drain each port before ide_exit() so no class of request can outlive the allocation. Delete check_bh there as well; qemu_bh_new_guarded() in check_cmd() has no counterpart on this path either. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4069 Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/ahci.c | 72 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 23 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 2b2ef873e0..6b04762c4a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -619,12 +619,37 @@ static void ahci_set_signature(AHCIDevice *ad, uint32_t sig) s->lcyl, s->hcyl, sig); } +static void ahci_cancel_ncq_requests(AHCIDevice *ad) +{ + int i; + + for (i = 0; i < AHCI_MAX_CMDS; i++) { + NCQTransferState *ncq_tfs = &ad->ncq_tfs[i]; + ncq_tfs->halt = false; + if (!ncq_tfs->used) { + continue; + } + + if (ncq_tfs->aiocb) { + blk_aio_cancel(ncq_tfs->aiocb); + ncq_tfs->aiocb = NULL; + } + + /* Maybe we just finished the request thanks to blk_aio_cancel() */ + if (!ncq_tfs->used) { + continue; + } + + qemu_sglist_destroy(&ncq_tfs->sglist); + ncq_tfs->used = 0; + } +} + static void ahci_reset_port(AHCIState *s, int port, IDEResetKind kind) { AHCIDevice *d = &s->dev[port]; AHCIPortRegs *pr = &d->port_regs; IDEState *ide_state = &d->port.ifs[0]; - int i; trace_ahci_reset_port(s, port); @@ -645,27 +670,7 @@ static void ahci_reset_port(AHCIState *s, int port, IDEResetKind kind) return; } - /* reset ncq queue */ - for (i = 0; i < AHCI_MAX_CMDS; i++) { - NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i]; - ncq_tfs->halt = false; - if (!ncq_tfs->used) { - continue; - } - - if (ncq_tfs->aiocb) { - blk_aio_cancel(ncq_tfs->aiocb); - ncq_tfs->aiocb = NULL; - } - - /* Maybe we just finished the request thanks to blk_aio_cancel() */ - if (!ncq_tfs->used) { - continue; - } - - qemu_sglist_destroy(&ncq_tfs->sglist); - ncq_tfs->used = 0; - } + ahci_cancel_ncq_requests(d); s->dev[port].port_state = STATE_RUN; if (ide_state->drive_kind == IDE_CD) { @@ -1659,8 +1664,29 @@ void ahci_uninit(AHCIState *s) for (i = 0; i < s->ports; i++) { AHCIDevice *ad = &s->dev[i]; + /* + * Unplug does not go through a reset, so this is the only chance to + * detach the requests and the bottom half that would otherwise walk + * s->dev after it is freed below. + */ + ahci_cancel_ncq_requests(ad); + if (ad->check_bh) { + qemu_bh_delete(ad->check_bh); + ad->check_bh = NULL; + } + for (j = 0; j < 2; j++) { - ide_exit(&ad->port.ifs[j]); + IDEState *ide_state = &ad->port.ifs[j]; + + /* + * Everything the port still owns points into the allocation this + * function frees, io_buffer included, so nothing may be left in + * flight once ide_exit() has run. + */ + if (ide_state->blk) { + blk_drain(ide_state->blk); + } + ide_exit(ide_state); } object_unparent(OBJECT(&ad->port)); } From 0b7cc42531eafa02742c783bd895035f3c867400 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 18 Aug 2026 09:55:48 +0200 Subject: [PATCH 858/860] tests/qtest/ahci: regression test for a request outliving an unplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add /ahci/io/{ncq,dma,pio}/unplug: arm a read against a null-co backend whose latency keeps it in flight, then eject the controller through the ACPI ejection register. Each of the three reaches the freed AHCIDevice array by a different route, so covering one command class would leave the other two untested. That register is what a guest writes to finish a PCI unplug, and unlike the pciehp attention button it reaches ahci_uninit() with no secondary bus reset, so nothing cancels the request on the way. It also dictates the machine: q35 has no ACPI hotplug on pcie.0, so the eject has no effect there. The latency is what holds the request; a blkdebug breakpoint cannot stand in for it, because cancelling a suspended request waits for it and the unplug would never return. Unfixed, all three fail reliably under AddressSanitizer. On a plain build the use-after-free only faults when the freed page has been returned, so expect the odd pass there. Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ahci-test.c | 75 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 84d4e6b0a5..b143862ce7 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -1793,6 +1793,78 @@ static void test_atapi_engine_restart_dma(void) test_atapi_engine_restart_in_flight(true); } +/* + * Regression test: an unplug runs no device reset, so it is the last chance to + * detach an outstanding request. Its completion would otherwise walk the + * AHCIDevice array that ahci_uninit() has freed, which each of the NCQ, DMA + * and PIO completions reaches by a different route. + * + * The ACPI ejection register is what a guest writes to finish a PCI unplug. + * -M pc is what puts it in reach: q35 has no ACPI hotplug on pcie.0, so the + * unplug never happens there. Unlike the pciehp attention button this reaches + * the unplug with no secondary bus reset, which is the ordering that leaves a + * request outstanding. + */ +static void test_unplug_in_flight(uint8_t ide_cmd) +{ + AHCIQState *ahci; + AHCICommand *cmd; + uint64_t ptr; + uint8_t port; + QTestState *qts; + + /* + * The latency keeps the backend read in flight across the unplug. A + * blkdebug breakpoint cannot stand in for it: cancelling a suspended + * request waits for it, so the unplug would never return. + */ + ahci = ahci_boot_and_enable( + "-M pc " + "-blockdev driver=null-co,node-name=drive0,read-zeroes=on," + "latency-ns=100000000 " + "-device ich9-ahci,addr=1f.2,id=ahci0 " + "-device ide-hd,drive=drive0,bus=ahci0.0 "); + qts = ahci->parent->qts; + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + ptr = ahci_alloc(ahci, AHCI_SECTOR_SIZE); + g_assert(ptr); + + cmd = ahci_command_create(ide_cmd); + ahci_command_adjust(cmd, 0, ptr, AHCI_SECTOR_SIZE, 0); + ahci_command_commit(ahci, cmd, port); + ahci_command_issue_async(ahci, cmd); + + /* Eject slot 0x1f of the root bus, which frees the AHCIDevice array. */ + qtest_outl(qts, 0xae10, 0); + qtest_outl(qts, 0xae08, 1u << 0x1f); + qtest_qmp_eventwait(qts, "DEVICE_DELETED"); + + /* Four times the backend latency, so the completion has surely run. */ + g_usleep(400 * 1000); + qtest_qmp_assert_success(qts, "{ 'execute': 'query-status' }"); + + ahci_command_free(cmd); + ahci_free(ahci, ptr); + ahci_shutdown(ahci); +} + +static void test_unplug_ncq(void) +{ + test_unplug_in_flight(READ_FPDMA_QUEUED); +} + +static void test_unplug_dma(void) +{ + test_unplug_in_flight(CMD_READ_DMA); +} + +static void test_unplug_pio(void) +{ + test_unplug_in_flight(CMD_READ_PIO); +} + /* * Regression test: a PIO write outlives the command list it was issued from. * ide_cancel_dma_sync() does not reach s->pio_aiocb, so the second DRQ phase @@ -2351,6 +2423,9 @@ int main(int argc, char **argv) test_atapi_engine_restart_pio); qtest_add_func("/ahci/cdrom/engine_restart/dma", test_atapi_engine_restart_dma); + qtest_add_func("/ahci/io/ncq/unplug", test_unplug_ncq); + qtest_add_func("/ahci/io/dma/unplug", test_unplug_dma); + qtest_add_func("/ahci/io/pio/unplug", test_unplug_pio); qtest_add_func("/ahci/io/pio/engine_stop", test_write_engine_stop_in_flight); qtest_add_func("/ahci/cdrom/drain/pio", test_atapi_drain_pio); From 60faaa8c3bd6b9b420adcb2530a9d5fbc52a0c5e Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 17 Aug 2026 23:44:59 +0200 Subject: [PATCH 859/860] hw/ide: report ATAPI UDMA5 with a matching standard and cable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IDENTIFY PACKET DEVICE claims UDMA mode 5 in word 88 while word 80 reports support only up to ATA/ATAPI-4. UDMA5 first appears in ATA/ATAPI-6; ATA/ATAPI-5 stops at mode 4. Bits 3:1 of word 80 are obsolete in IDENTIFY PACKET DEVICE data as well, so the old 001eh claimed three standards that mean nothing for a packet device. Report 0070h, ATA/ATAPI-4 through ATA/ATAPI-6. Word 93 was left unset, so nothing reported the 80-conductor cable that UDMA5 needs. Fill it in, but only for a parallel attachment: ACS-3 7.13.6.41 gives word 93 of IDENTIFY PACKET DEVICE data the meaning of word 93 of IDENTIFY DEVICE data, where "For SATA devices, word 93 shall be set to the value 0000h". A cleared ncq_queues is how both identify paths already tell a parallel attachment from an AHCI one. The device 0 reset result is 0fh rather than the 01h ide_identify() reports: bit 3 says diagnostics passed, which they did, and bits 2:1 say the device number came from some other method, the only one of the four encodings that is not a jumper, CSEL or reserved. Raising word 80 has a second effect. Linux decides a device is SATA in ata_id_is_sata(), which wants word 93 clear and word 80 at ATA/ATAPI-5 or later. An AHCI CD-ROM satisfied neither condition before and was taken for a parallel device; now it satisfies both. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/4038 Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- hw/ide/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 06c18dbf09..ef573798d9 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -292,7 +292,7 @@ static void ide_atapi_identify(IDEState *s) put_le16(p + 76, (1 << 8)); } - put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ + put_le16(p + 80, 0x70); /* support up to ATA/ATAPI-6 */ if (s->wwn) { put_le16(p + 84, (1 << 8)); /* supports WWN for words 108-111 */ put_le16(p + 87, (1 << 8)); /* WWN enabled */ @@ -300,6 +300,10 @@ static void ide_atapi_identify(IDEState *s) #ifdef USE_DMA_CDROM put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ + if (!s->ncq_queues) { + /* word 93 is parallel ATA only, a SATA device reports zero */ + put_le16(p + 93, 0x600f); + } #endif if (s->wwn) { From dd90d3cd73b9b2b0f27a9e51bfe542b3a510545b Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Tue, 18 Aug 2026 01:05:36 +0200 Subject: [PATCH 860/860] tests/qtest/ide-test: cover the UDMA5 identify words MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /ide/identify/udma and /ide/identify/udma_atapi check that a device advertising UDMA mode 5 claims a standard that defines it and reports the hardware reset result, on the disk and on the CD-ROM. The ATAPI case also checks that the words obsolete in IDENTIFY PACKET DEVICE data stay clear, and that the reset result reports a passed diagnostic, which only the packet path does so far. Cc: John Snow Cc: Philippe Mathieu-Daudé Signed-off-by: Denis V. Lunev --- tests/qtest/ide-test.c | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index a3109da908..92e3d9b343 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -104,6 +104,7 @@ enum { CMD_FLUSH_CACHE = 0xe7, CMD_IDENTIFY = 0xec, CMD_PACKET = 0xa0, + CMD_IDENTIFY_PACKET = 0xa1, CMD_READ_NATIVE = 0xf8, /* READ NATIVE MAX ADDRESS */ CMDF_ABORT = 0x100, @@ -1571,6 +1572,72 @@ static void test_migrate_chs_rejected(void) unlink(path); } +/* + * A device advertising UDMA5 has to claim a standard that defines it, and a + * parallel attachment has to report the cable word (ACS-3 7.12.7.47). + */ +static void test_identify_udma(bool packet) +{ + QTestState *qts; + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + uint16_t buf[256]; + int i; + + if (packet) { + qts = ide_test_start("-device ide-cd,bus=ide.0"); + } else { + qts = ide_test_start( + "-blockdev driver=file,node-name=hda,filename=%s " + "-device ide-hd,drive=hda,bus=ide.0,unit=0 ", + tmp_path[0]); + } + dev = get_pci_device(qts, &bmdma_bar, &ide_bar); + + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, + packet ? CMD_IDENTIFY_PACKET : CMD_IDENTIFY); + for (i = 0; i < 256; i++) { + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); + } + + /* UDMA5 supported and selected */ + assert_bit_set(buf[88], 1 << 5); + assert_bit_set(buf[88], 1 << 13); + + /* UDMA5 arrived in ATA/ATAPI-6, so word 80 has to reach bit 6 */ + assert_bit_set(buf[80], 1 << 6); + if (packet) { + /* Bits 3:1 are obsolete in IDENTIFY PACKET DEVICE data */ + assert_bit_clear(buf[80], 0x0e); + } + + /* Word 93: reserved bit clear, fixed bit set, 80-conductor cable */ + assert_bit_clear(buf[93], 1 << 15); + assert_bit_set(buf[93], 1 << 14); + assert_bit_set(buf[93], 1 << 13); + assert_bit_set(buf[93], 1 << 0); + /* Device 0 clears the device 1 result */ + assert_bit_clear(buf[93], 0x1f00); + if (packet) { + /* the disk path has yet to gain this */ + assert_bit_set(buf[93], 1 << 3); + } + + free_pci_device(dev); + ide_test_quit(qts); +} + +static void test_identify_udma_ata(void) +{ + test_identify_udma(false); +} + +static void test_identify_udma_atapi(void) +{ + test_identify_udma(true); +} + /* A PIO transfer window reaching past the io_buffer has to be refused */ static void test_migrate_pio_state_rejected(void) { @@ -1843,6 +1910,8 @@ int main(int argc, char **argv) qtest_add_func("/ide/migration/chs_rejected", test_migrate_chs_rejected); qtest_add_func("/ide/migration/pio_state_rejected", test_migrate_pio_state_rejected); + qtest_add_func("/ide/identify/udma", test_identify_udma_ata); + qtest_add_func("/ide/identify/udma_atapi", test_identify_udma_atapi); qtest_add_func("/ide/identify", test_identify);

j_dnA{ckfU@|Pb@?L2E;965Hc-xi|^{f?8 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 x~^*4y38OA#=F{1 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 z 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 xh$?aACnZ`WO(~&zTWV;9{+lbV14mRw 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