diff options
72 files changed, 1114 insertions, 332 deletions
@@ -67,6 +67,7 @@ Andrey Drobyshev <andrey.drobyshev@virtuozzo.com> Andrey Drobyshev via <qemu-blo BALATON Zoltan <balaton@eik.bme.hu> BALATON Zoltan via <qemu-ppc@nongnu.org> # Next, replace old addresses by a more recent one. +Akihiko Odaki <akihiko.odaki@daynix.com> <akihiko.odaki@gmail.com> Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com> Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgtec.com> Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com> @@ -207,10 +207,10 @@ clean: recurse-clean VERSION = $(shell cat $(SRC_PATH)/VERSION) -dist: qemu-$(VERSION).tar.bz2 +dist: qemu-$(VERSION).tar.xz -qemu-%.tar.bz2: - $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" +qemu-%.tar.xz: + $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.xz,%,$@)" distclean: clean recurse-distclean -$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || : diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c index 41cf24b..352c3e8 100644 --- a/backends/cryptodev-lkcf.c +++ b/backends/cryptodev-lkcf.c @@ -330,6 +330,8 @@ static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) cryptodev_lkcf_set_op_desc(&session->akcipher_opts, op_desc, sizeof(op_desc), &local_error) != 0) { error_report_err(local_error); + status = -VIRTIO_CRYPTO_ERR; + goto out; } else { key_id = add_key(KCTL_KEY_TYPE_PKEY, "lkcf-backend-priv-key", p8info, p8info_len, KCTL_KEY_RING); @@ -346,6 +348,7 @@ static void cryptodev_lkcf_execute_task(CryptoDevLKCFTask *task) session->key, session->keylen, &local_error); if (!akcipher) { + error_report_err(local_error); status = -VIRTIO_CRYPTO_ERR; goto out; } diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 833fa3b..3bca0cc 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -44,7 +44,7 @@ static inline void memcpy_fromfs(void *to, const void *from, unsigned long n) memcpy(to, from, n); } -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_ehdr(struct elfhdr *ehdr) { bswap16s(&ehdr->e_type); /* Object file type */ @@ -111,7 +111,7 @@ static void bswap_note(struct elf_note *en) bswap32s(&en->n_type); } -#else /* ! BSWAP_NEEDED */ +#else static void bswap_ehdr(struct elfhdr *ehdr) { } static void bswap_phdr(struct elf_phdr *phdr, int phnum) { } @@ -119,7 +119,7 @@ static void bswap_shdr(struct elf_shdr *shdr, int shnum) { } static void bswap_sym(struct elf_sym *sym) { } static void bswap_note(struct elf_note *en) { } -#endif /* ! BSWAP_NEEDED */ +#endif /* HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN */ #include "elfcore.c" diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak index 9ef343c..4faf2f0 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -6,7 +6,6 @@ #CONFIG_APPLESMC=n #CONFIG_FDC=n #CONFIG_HPET=n -#CONFIG_X_HPET_RUST=n #CONFIG_HYPERV=n #CONFIG_ISA_DEBUG=n #CONFIG_ISA_IPMI_BT=n diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 57f55f6..745d21d 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -214,13 +214,23 @@ # PL011 UART. @verbose-static is mutually exclusive # with @verbose-dynamic. # +# @host-uefi-vars: The firmware expects the host to provide an uefi +# variable store. qemu supports that via +# "uefi-vars-sysbus" (aarch64, riscv64, loongarch64) +# or "uefi-vars-x64" (x86_64) devices. The firmware +# will not use flash for nvram. When loading the +# firmware into flash the 'stateless' setup should be +# used. It is recommened to load the firmware into +# memory though. +# # Since: 3.0 ## { 'enum' : 'FirmwareFeature', 'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'amd-sev-es', 'amd-sev-snp', 'intel-tdx', - 'enrolled-keys', 'requires-smm', 'secure-boot', + 'enrolled-keys', 'requires-smm', + 'secure-boot', 'host-uefi-vars', 'verbose-dynamic', 'verbose-static' ] } ## diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index 2ab516d..a3efbdc 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -77,6 +77,106 @@ When using the ``'-netdev user,hostfwd=...'`` option, TCP or UDP connections can be redirected from the host to the guest. It allows for example to redirect X11, telnet or SSH connections. +Using passt as the user mode network stack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +passt_ can be used as a simple replacement for SLIRP (``-net user``). +passt doesn't require any capability or privilege. passt has +better performance than ``-net user``, full IPv6 support and better security +as it's a daemon that is not executed in QEMU context. + +passt can be connected to QEMU either by using a socket +(``-netdev stream``) or using the vhost-user interface (``-netdev vhost-user``). +See `passt(1)`_ for more details on passt. + +.. _passt: https://passt.top/ +.. _passt(1): https://passt.top/builds/latest/web/passt.1.html + +To use socket based passt interface: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Start passt as a daemon:: + + passt --socket ~/passt.socket + +If ``--socket`` is not provided, passt will print the path of the UNIX domain socket QEMU can connect to (``/tmp/passt_1.socket``, ``/tmp/passt_2.socket``, +...). Then you can connect your QEMU instance to passt: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -device virtio-net-pci,netdev=netdev0 -netdev stream,id=netdev0,server=off,addr.type=unix,addr.path=~/passt.socket + +Where ``~/passt.socket`` is the UNIX socket created by passt to +communicate with QEMU. + +To use vhost-based interface: +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Start passt with ``--vhost-user``:: + + passt --vhost-user --socket ~/passt.socket + +Then to connect QEMU: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -m $RAMSIZE -chardev socket,id=chr0,path=~/passt.socket -netdev vhost-user,id=netdev0,chardev=chr0 -device virtio-net,netdev=netdev0 -object memory-backend-memfd,id=memfd0,share=on,size=$RAMSIZE -numa node,memdev=memfd0 + +Where ``$RAMSIZE`` is the memory size of your VM ``-m`` and ``-object memory-backend-memfd,size=`` must match. + +Migration of passt: +^^^^^^^^^^^^^^^^^^^ + +When passt is connected to QEMU using the vhost-user interface it can +be migrated with QEMU and the network connections are not interrupted. + +As passt runs with no privileges, it relies on passt-repair to save and +load the TCP connections state, using the TCP_REPAIR socket option. +The passt-repair helper needs to have the CAP_NET_ADMIN capability, or run as root. If passt-repair is not available, TCP connections will not be preserved. + +Example of migration of a guest on the same host +________________________________________________ + +Before being able to run passt-repair, the CAP_NET_ADMIN capability must be set +on the file, run as root:: + + setcap cap_net_admin+eip ./passt-repair + +Start passt for the source side:: + + passt --vhost-user --socket ~/passt_src.socket --repair-path ~/passt-repair_src.socket + +Where ``~/passt-repair_src.socket`` is the UNIX socket created by passt to +communicate with passt-repair. The default value is the ``--socket`` path +appended with ``.repair``. + +Start passt-repair:: + + passt-repair ~/passt-repair_src.socket + +Start source side QEMU with a monitor to be able to send the migrate command: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] [...VHOST USER OPTIONS...] -monitor stdio + +Start passt for the destination side:: + + passt --vhost-user --socket ~/passt_dst.socket --repair-path ~/passt-repair_dst.socket + +Start passt-repair:: + + passt-repair ~/passt-repair_dst.socket + +Start QEMU with the ``-incoming`` parameter: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] [...VHOST USER OPTIONS...] -incoming tcp:localhost:4444 + +Then in the source guest monitor the migration can be started:: + + (qemu) migrate tcp:localhost:4444 + +A separate passt-repair instance must be started for every migration. In the case of a failed migration, passt-repair also needs to be restarted before trying +again. + Hubs ~~~~ diff --git a/docs/system/loongarch/virt.rst b/docs/system/loongarch/virt.rst index 172fba0..7845878 100644 --- a/docs/system/loongarch/virt.rst +++ b/docs/system/loongarch/virt.rst @@ -12,14 +12,15 @@ Supported devices ----------------- The ``virt`` machine supports: -- Gpex host bridge -- Ls7a RTC device -- Ls7a IOAPIC device -- ACPI GED device -- Fw_cfg device -- PCI/PCIe devices -- Memory device -- CPU device. Type: la464. + +* Gpex host bridge +* Ls7a RTC device +* Ls7a IOAPIC device +* ACPI GED device +* Fw_cfg device +* PCI/PCIe devices +* Memory device +* CPU device. Type: la464. CPU and machine Type -------------------- @@ -39,13 +40,7 @@ can be accessed by following steps. .. code-block:: bash - ./configure --disable-rdma --prefix=/usr \ - --target-list="loongarch64-softmmu" \ - --disable-libiscsi --disable-libnfs --disable-libpmem \ - --disable-glusterfs --enable-libusb --enable-usb-redir \ - --disable-opengl --disable-xen --enable-spice \ - --enable-debug --disable-capstone --disable-kvm \ - --enable-profiler + ./configure --target-list="loongarch64-softmmu" make -j8 (2) Set cross tools: @@ -53,9 +48,7 @@ can be accessed by following steps. .. code-block:: bash wget https://github.com/loongson/build-tools/releases/download/2022.09.06/loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz - tar -vxf loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz -C /opt - export PATH=/opt/cross-tools/bin:$PATH export LD_LIBRARY_PATH=/opt/cross-tools/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=/opt/cross-tools/loongarch64-unknown-linux-gnu/lib/:$LD_LIBRARY_PATH @@ -74,13 +67,9 @@ Note: To build the release version of the bios, set --buildtarget=RELEASE, .. code-block:: bash git clone https://github.com/loongson/linux.git - cd linux - git checkout loongarch-next - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- loongson3_defconfig - make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- -j32 Note: The branch of linux source code is loongarch-next. diff --git a/docs/system/target-loongarch.rst b/docs/system/target-loongarch.rst new file mode 100644 index 0000000..316c604 --- /dev/null +++ b/docs/system/target-loongarch.rst @@ -0,0 +1,19 @@ +.. _LoongArch-System-emulator: + +LoongArch System emulator +------------------------- + +QEMU can emulate loongArch 64 bit systems via the +``qemu-system-loongarch64`` binary. Only one machine type ``virt`` is +supported. + +When using KVM as accelerator, QEMU can emulate la464 cpu model. And when +using the default cpu model with TCG as accelerator, QEMU will emulate a +subset of la464 cpu features that should be enough to run distributions +built for the la464. + +Board-specific documentation +============================ + +.. toctree:: + loongarch/virt diff --git a/docs/system/targets.rst b/docs/system/targets.rst index 224fada..38e2418 100644 --- a/docs/system/targets.rst +++ b/docs/system/targets.rst @@ -18,6 +18,7 @@ Contents: target-arm target-avr + target-loongarch target-m68k target-mips target-ppc diff --git a/host/include/loongarch64/host/atomic128-ldst.h.inc b/host/include/loongarch64/host/atomic128-ldst.h.inc index 9a4a8f8..754d214 100644 --- a/host/include/loongarch64/host/atomic128-ldst.h.inc +++ b/host/include/loongarch64/host/atomic128-ldst.h.inc @@ -28,7 +28,7 @@ static inline Int128 atomic16_read_ro(const Int128 *ptr) asm("vld $vr0, %2, 0\n\t" "vpickve2gr.d %0, $vr0, 0\n\t" "vpickve2gr.d %1, $vr0, 1" - : "=r"(l), "=r"(h) : "r"(ptr), "m"(*ptr) : "f0"); + : "=r"(l), "=r"(h) : "r"(ptr), "m"(*ptr) : "$f0"); return int128_make128(l, h); } @@ -46,7 +46,7 @@ static inline void atomic16_set(Int128 *ptr, Int128 val) asm("vinsgr2vr.d $vr0, %1, 0\n\t" "vinsgr2vr.d $vr0, %2, 1\n\t" "vst $vr0, %3, 0" - : "=m"(*ptr) : "r"(l), "r"(h), "r"(ptr) : "f0"); + : "=m"(*ptr) : "r"(l), "r"(h), "r"(ptr) : "$f0"); } #endif /* LOONGARCH_ATOMIC128_LDST_H */ diff --git a/host/include/loongarch64/host/bufferiszero.c.inc b/host/include/loongarch64/host/bufferiszero.c.inc index 69891ea..bb2598f 100644 --- a/host/include/loongarch64/host/bufferiszero.c.inc +++ b/host/include/loongarch64/host/bufferiszero.c.inc @@ -61,7 +61,8 @@ static bool buffer_is_zero_lsx(const void *buf, size_t len) "2:" : "=&r"(ret), "+r"(p) : "r"(buf), "r"(e), "r"(l) - : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + : "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", + "$fcc0"); return ret; } @@ -119,7 +120,8 @@ static bool buffer_is_zero_lasx(const void *buf, size_t len) "3:" : "=&r"(ret), "+r"(p) : "r"(buf), "r"(e), "r"(l) - : "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "fcc0"); + : "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", + "$fcc0"); return ret; } diff --git a/host/include/loongarch64/host/load-extract-al16-al8.h.inc b/host/include/loongarch64/host/load-extract-al16-al8.h.inc index d1fb59d..9528521 100644 --- a/host/include/loongarch64/host/load-extract-al16-al8.h.inc +++ b/host/include/loongarch64/host/load-extract-al16-al8.h.inc @@ -31,7 +31,7 @@ static inline uint64_t load_atom_extract_al16_or_al8(void *pv, int s) asm("vld $vr0, %2, 0\n\t" "vpickve2gr.d %0, $vr0, 0\n\t" "vpickve2gr.d %1, $vr0, 1" - : "=r"(l), "=r"(h) : "r"(ptr_align), "m"(*ptr_align) : "f0"); + : "=r"(l), "=r"(h) : "r"(ptr_align), "m"(*ptr_align) : "$f0"); return (l >> shr) | (h << (-shr & 63)); } diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 15200a2..a55b44d 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -21,8 +21,7 @@ config ARM_VIRT select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL061 # GPIO select GPIO_PWR @@ -75,8 +74,7 @@ config HIGHBANK select AHCI_SYSBUS select ARM_TIMER # sp804 select ARM_V7M - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL022 # SPI select PL031 # RTC select PL061 # GPIO @@ -89,8 +87,7 @@ config INTEGRATOR depends on TCG && ARM select ARM_TIMER select INTEGRATOR_DEBUG - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL041 # audio select PL050 # keyboard/mouse @@ -108,8 +105,7 @@ config MUSCA default y depends on TCG && ARM select ARMSSE - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 select SPLIT_IRQ select UNIMP @@ -173,8 +169,7 @@ config REALVIEW select WM8750 # audio codec select LSI_SCSI_PCI select PCI - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL041 # audio codec select PL050 # keyboard/mouse @@ -199,8 +194,7 @@ config SBSA_REF select PCI_EXPRESS select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL031 # RTC select PL061 # GPIO select USB_XHCI_SYSBUS @@ -224,8 +218,7 @@ config STELLARIS select ARM_V7M select CMSDK_APB_WATCHDOG select I2C - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL022 # SPI select PL061 # GPIO select SSD0303 # OLED display @@ -285,8 +278,7 @@ config VEXPRESS select ARM_TIMER # sp804 select LAN9118 select PFLASH_CFI01 - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select PL041 # audio codec select PL181 # display select REALVIEW @@ -371,8 +363,7 @@ config RASPI default y depends on TCG && ARM select FRAMEBUFFER - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select SDHCI select USB_DWC2 select BCM2835_SPI @@ -448,8 +439,7 @@ config XLNX_VERSAL select ARM_GIC select CPU_CLUSTER select DEVICE_TREE - select PL011 if !HAVE_RUST # UART - select X_PL011_RUST if HAVE_RUST # UART + select PL011 # UART select CADENCE select VIRTIO_MMIO select UNIMP diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 3f70256..9d517f3 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -11,6 +11,12 @@ config PARALLEL config PL011 bool + # The PL011 has both a Rust and a C implementation + select PL011_C if !HAVE_RUST + select X_PL011_RUST if HAVE_RUST + +config PL011_C + bool config SERIAL bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 86ee808..4e439da 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -9,7 +9,7 @@ system_ss.add(when: 'CONFIG_ISA_BUS', if_true: files('parallel-isa.c')) system_ss.add(when: 'CONFIG_ISA_DEBUG', if_true: files('debugcon.c')) system_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_uart.c')) system_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.c')) -system_ss.add(when: 'CONFIG_PL011', if_true: files('pl011.c')) +system_ss.add(when: 'CONFIG_PL011_C', if_true: files('pl011.c')) system_ss.add(when: 'CONFIG_SCLPCONSOLE', if_true: files('sclpconsole.c', 'sclpconsole-lm.c')) system_ss.add(when: 'CONFIG_SERIAL', if_true: files('serial.c')) system_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c')) diff --git a/hw/core/loader.c b/hw/core/loader.c index ce6ff1b..2e35f0a 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -226,7 +226,7 @@ static void bswap_ahdr(struct exec *e) ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size) + bool big_endian, hwaddr target_page_size) { int fd; ssize_t size, ret; @@ -241,7 +241,7 @@ ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, if (size < 0) goto fail; - if (bswap_needed) { + if (big_endian != HOST_BIG_ENDIAN) { bswap_ahdr(&e); } diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index c04df3b..147b3ff 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -442,6 +442,43 @@ const PropertyInfo qdev_prop_uint64_checkmask = { .set = set_uint64_checkmask, }; +/* --- pointer-size integer --- */ + +static void get_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +static void set_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +const PropertyInfo qdev_prop_usize = { + .type = "usize", + .get = get_usize, + .set = set_usize, + .set_default_value = qdev_propinfo_set_default_value_uint, +}; + /* --- string --- */ static void release_string(Object *obj, const char *name, void *opaque) diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index a7f1b60..5c0bcd5 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -26,7 +26,7 @@ #include CONFIG_DEVICES #include "target/i386/cpu.h" -#if !defined(CONFIG_HPET) && !defined(CONFIG_X_HPET_RUST) +#if !defined(CONFIG_HPET) struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; #endif diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 63a96cd..01d0581 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1704,7 +1704,7 @@ static void pc_machine_initfn(Object *obj) pcms->sata_enabled = true; pcms->i8042_enabled = true; pcms->max_fw_size = 8 * MiB; -#if defined(CONFIG_HPET) || defined(CONFIG_X_HPET_RUST) +#if defined(CONFIG_HPET) pcms->hpet_enabled = true; #endif pcms->fd_bootchk = true; diff --git a/hw/intc/xive.c b/hw/intc/xive.c index c77df2c..3eb28c2 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1662,12 +1662,20 @@ uint32_t xive_get_vpgroup_size(uint32_t nvp_index) * (starting with the least significant bits) in the NVP index * gives the size of the group. */ - return 1 << (ctz32(~nvp_index) + 1); + int first_zero = cto32(nvp_index); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group index 0x%08x", + nvp_index); + return 0; + } + + return 1U << (first_zero + 1); } static uint8_t xive_get_group_level(bool crowd, bool ignore, uint32_t nvp_blk, uint32_t nvp_index) { + int first_zero; uint8_t level; if (!ignore) { @@ -1675,18 +1683,31 @@ static uint8_t xive_get_group_level(bool crowd, bool ignore, return 0; } - level = (ctz32(~nvp_index) + 1) & 0b1111; + first_zero = cto32(nvp_index); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group index 0x%08x", + nvp_index); + return 0; + } + + level = (first_zero + 1) & 0b1111; if (crowd) { uint32_t blk; /* crowd level is bit position of first 0 from the right in nvp_blk */ - blk = ctz32(~nvp_blk) + 1; + first_zero = cto32(nvp_blk); + if (first_zero >= 31) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd block 0x%08x", + nvp_blk); + return 0; + } + blk = first_zero + 1; /* * Supported crowd sizes are 2^1, 2^2, and 2^4. 2^3 is not supported. * HW will encode level 4 as the value 3. See xive2_pgofnext(). */ - switch (level) { + switch (blk) { case 1: case 2: break; diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f8ef615..7d584df 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1153,13 +1153,15 @@ static bool xive2_vp_match_mask(uint32_t cam1, uint32_t cam2, static uint8_t xive2_get_vp_block_mask(uint32_t nvt_blk, bool crowd) { - uint8_t size, block_mask = 0b1111; + uint8_t block_mask = 0b1111; /* 3 supported crowd sizes: 2, 4, 16 */ if (crowd) { - size = xive_get_vpgroup_size(nvt_blk); - if (size == 8) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd size of 8n"); + uint32_t size = xive_get_vpgroup_size(nvt_blk); + + if (size != 2 && size != 4 && size != 16) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid crowd size of %d", + size); return block_mask; } block_mask &= ~(size - 1); @@ -1172,7 +1174,14 @@ static uint32_t xive2_get_vp_index_mask(uint32_t nvt_index, bool cam_ignore) uint32_t index_mask = 0xFFFFFF; /* 24 bits */ if (cam_ignore) { - index_mask &= ~(xive_get_vpgroup_size(nvt_index) - 1); + uint32_t size = xive_get_vpgroup_size(nvt_index); + + if (size < 2) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid group size of %d", + size); + return index_mask; + } + index_mask &= ~(size - 1); } return index_mask; } @@ -1335,7 +1344,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - if (xive2_end_is_crowd(&end) & !xive2_end_is_ignore(&end)) { + if (xive2_end_is_crowd(&end) && !xive2_end_is_ignore(&end)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid END, 'crowd' bit requires 'ignore' bit\n"); return; diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a5840ff..e258642 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -38,6 +38,7 @@ #include "hw/mem/nvdimm.h" #include "hw/platform-bus.h" #include "hw/display/ramfb.h" +#include "hw/uefi/var-service-api.h" #include "hw/mem/pc-dimm.h" #include "system/tpm.h" #include "system/block-backend.h" @@ -327,7 +328,6 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *possible_cpus; CPUState *cs; - Error *err = NULL; /* cpu nodes */ possible_cpus = mc->possible_cpu_arch_ids(ms); @@ -337,8 +337,10 @@ static void virt_cpu_irq_init(LoongArchVirtMachineState *lvms) continue; } - hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), &err); - hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), &err); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->ipi), DEVICE(cs), + &error_abort); + hotplug_handler_plug(HOTPLUG_HANDLER(lvms->extioi), DEVICE(cs), + &error_abort); } } @@ -859,30 +861,29 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, LoongArchCPU *cpu = LOONGARCH_CPU(dev); CPUState *cs = CPU(dev); CPUArchId *cpu_slot; - Error *err = NULL; LoongArchCPUTopo topo; int arch_id; if (lvms->acpi_ged) { if ((cpu->thread_id < 0) || (cpu->thread_id >= ms->smp.threads)) { - error_setg(&err, + error_setg(errp, "Invalid thread-id %u specified, must be in range 1:%u", cpu->thread_id, ms->smp.threads - 1); - goto out; + return; } if ((cpu->core_id < 0) || (cpu->core_id >= ms->smp.cores)) { - error_setg(&err, + error_setg(errp, "Invalid core-id %u specified, must be in range 1:%u", cpu->core_id, ms->smp.cores - 1); - goto out; + return; } if ((cpu->socket_id < 0) || (cpu->socket_id >= ms->smp.sockets)) { - error_setg(&err, + error_setg(errp, "Invalid socket-id %u specified, must be in range 1:%u", cpu->socket_id, ms->smp.sockets - 1); - goto out; + return; } topo.socket_id = cpu->socket_id; @@ -891,11 +892,11 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, arch_id = virt_get_arch_id_from_topo(ms, &topo); cpu_slot = virt_find_cpu_slot(ms, arch_id); if (CPU(cpu_slot->cpu)) { - error_setg(&err, + error_setg(errp, "cpu(id%d=%d:%d:%d) with arch-id %" PRIu64 " exists", cs->cpu_index, cpu->socket_id, cpu->core_id, cpu->thread_id, cpu_slot->arch_id); - goto out; + return; } } else { /* For cold-add cpu, find empty cpu slot */ @@ -911,33 +912,24 @@ static void virt_cpu_pre_plug(HotplugHandler *hotplug_dev, cpu->env.address_space_iocsr = &lvms->as_iocsr; cpu->phy_id = cpu_slot->arch_id; cs->cpu_index = cpu_slot - ms->possible_cpus->cpus; - numa_cpu_pre_plug(cpu_slot, dev, &err); -out: - if (err) { - error_propagate(errp, err); - } + numa_cpu_pre_plug(cpu_slot, dev, errp); } static void virt_cpu_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(hotplug_dev); - Error *err = NULL; LoongArchCPU *cpu = LOONGARCH_CPU(dev); CPUState *cs = CPU(dev); if (cs->cpu_index == 0) { - error_setg(&err, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", + error_setg(errp, "hot-unplug of boot cpu(id%d=%d:%d:%d) not supported", cs->cpu_index, cpu->socket_id, cpu->core_id, cpu->thread_id); - error_propagate(errp, err); return; } - hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, &err); - if (err) { - error_propagate(errp, err); - } + hotplug_handler_unplug_request(HOTPLUG_HANDLER(lvms->acpi_ged), dev, errp); } static void virt_cpu_unplug(HotplugHandler *hotplug_dev, @@ -1207,6 +1199,7 @@ static void virt_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "v-eiointc", "Enable Virt Extend I/O Interrupt Controller."); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index 4835121..e9407a5 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -63,7 +63,7 @@ static const char dummy_fw[] = { #define NVRAM_ADDR 0xfd0e0000 #define NVRAM_SIZE (4 * KiB) -static char default_env[] = +static const char default_env[] = "baudrate=115200\0" "stdout=vga\0" "stdin=ps2kbd\0" @@ -108,8 +108,8 @@ static void nvram_write(void *opaque, hwaddr addr, uint64_t val, uint8_t *p = memory_region_get_ram_ptr(&s->mr); p[addr] = val; - if (s->blk) { - blk_pwrite(s->blk, addr, 1, &val, 0); + if (s->blk && blk_pwrite(s->blk, addr, 1, &val, 0) < 0) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); } } @@ -151,15 +151,17 @@ static void nvram_realize(DeviceState *dev, Error **errp) *c = cpu_to_be32(CRC32_DEFAULT_ENV); /* Also copies terminating \0 as env is terminated by \0\0 */ memcpy(p + 4, default_env, sizeof(default_env)); - if (s->blk) { - blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0); + if (s->blk && + blk_pwrite(s->blk, 0, sizeof(crc) + sizeof(default_env), p, 0) < 0 + ) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); } return; } if (*c == 0) { *c = cpu_to_be32(crc32(0, p + 4, NVRAM_SIZE - 4)); - if (s->blk) { - blk_pwrite(s->blk, 0, 4, p, 0); + if (s->blk && blk_pwrite(s->blk, 0, 4, p, 0) < 0) { + error_report("%s: could not write %s", __func__, blk_name(s->blk)); } } if (be32_to_cpu(*c) != crc) { diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index cb3dc3a..2d5309d 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -197,11 +197,6 @@ static void ppc_core99_init(MachineState *machine) } if (machine->kernel_filename) { - int bswap_needed = 0; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, @@ -209,7 +204,7 @@ static void ppc_core99_init(MachineState *machine) if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, - bswap_needed, TARGET_PAGE_SIZE); + true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 0dbcea0..b581469 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -153,11 +153,6 @@ static void ppc_heathrow_init(MachineState *machine) } if (machine->kernel_filename) { - int bswap_needed = 0; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(machine->kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, @@ -165,7 +160,7 @@ static void ppc_heathrow_init(MachineState *machine) if (kernel_size < 0) { kernel_size = load_aout(machine->kernel_filename, kernel_base, machine->ram_size - kernel_base, - bswap_needed, TARGET_PAGE_SIZE); + true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 5936537..63f2232 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1191,7 +1191,7 @@ static void pnv_init(MachineState *machine) * Since we can not reach the remote BMC machine with LPC memops, * map it always for now. */ - memory_region_add_subregion(pnv->chips[0]->fw_mr, PNOR_SPI_OFFSET, + memory_region_add_subregion(pnv->chips[0]->fw_mr, pnv->pnor->lpc_address, &pnv->pnor->mmio); /* diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index 811ba3d..fb70a8c 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -174,8 +174,8 @@ static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, { PnvPnor *pnor = PNV_PNOR(object_property_get_link(OBJECT(ibs), "pnor", &error_abort)); + uint32_t pnor_addr = pnor->lpc_address; uint32_t pnor_size = pnor->size; - uint32_t pnor_addr = PNOR_SPI_OFFSET; bool readonly = false; rsp_buffer_push(rsp, cmd[2]); @@ -251,8 +251,8 @@ static const IPMINetfn hiomap_netfn = { void pnv_bmc_set_pnor(IPMIBmc *bmc, PnvPnor *pnor) { + uint32_t pnor_addr = pnor->lpc_address; uint32_t pnor_size = pnor->size; - uint32_t pnor_addr = PNOR_SPI_OFFSET; if (!pnv_bmc_is_simulator(bmc)) { return; diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 99d9644..a33977d 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -248,21 +248,25 @@ static void pnv_core_power10_xscom_write(void *opaque, hwaddr addr, if (val & PPC_BIT(7 + 8 * i)) { /* stop */ val &= ~PPC_BIT(7 + 8 * i); - cpu_pause(cs); env->quiesced = true; + ppc_maybe_interrupt(env); + cpu_pause(cs); } if (val & PPC_BIT(6 + 8 * i)) { /* start */ val &= ~PPC_BIT(6 + 8 * i); env->quiesced = false; + ppc_maybe_interrupt(env); cpu_resume(cs); } if (val & PPC_BIT(4 + 8 * i)) { /* sreset */ val &= ~PPC_BIT(4 + 8 * i); env->quiesced = false; + ppc_maybe_interrupt(env); pnv_cpu_do_nmi_resume(cs); } if (val & PPC_BIT(3 + 8 * i)) { /* clear maint */ env->quiesced = false; + ppc_maybe_interrupt(env); /* * Hardware has very particular cases for where clear maint * must be used and where start must be used to resume a diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index bda6b23..177c5e5 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -364,7 +364,12 @@ static void pnv_occ_register_types(void) type_init(pnv_occ_register_types); -/* From skiboot/hw/occ.c with tab to space conversion */ +/* + * From skiboot/hw/occ.c with following changes: + * - tab to space conversion + * - Type conversions u8->uint8_t s8->int8_t __be16->uint16_t etc + * - __packed -> QEMU_PACKED + */ /* OCC Communication Area for PStates */ #define OPAL_DYNAMIC_DATA_OFFSET 0x0B80 @@ -384,20 +389,6 @@ type_init(pnv_occ_register_types); #define FREQ_MAX_IN_DOMAIN 0 #define FREQ_MOST_RECENTLY_SET 1 -#define u8 uint8_t -#define s8 int8_t -#define u16 uint16_t -#define s16 int16_t -#define u32 uint32_t -#define s32 int32_t -#define u64 uint64_t -#define s64 int64_t -#define __be16 uint16_t -#define __be32 uint32_t -#ifndef __packed -#define __packed QEMU_PACKED -#endif /* !__packed */ - /** * OCC-OPAL Shared Memory Region * @@ -434,69 +425,69 @@ type_init(pnv_occ_register_types); * @spare/reserved/pad: Unused data */ struct occ_pstate_table { - u8 valid; - u8 version; - union __packed { - struct __packed { /* Version 0x01 and 0x02 */ - u8 throttle; - s8 pstate_min; - s8 pstate_nom; - s8 pstate_turbo; - s8 pstate_ultra_turbo; - u8 spare; - u64 reserved; - struct __packed { - s8 id; - u8 flags; - u8 vdd; - u8 vcs; - __be32 freq_khz; + uint8_t valid; + uint8_t version; + union QEMU_PACKED { + struct QEMU_PACKED { /* Version 0x01 and 0x02 */ + uint8_t throttle; + int8_t pstate_min; + int8_t pstate_nom; + int8_t pstate_turbo; + int8_t pstate_ultra_turbo; + uint8_t spare; + uint64_t reserved; + struct QEMU_PACKED { + int8_t id; + uint8_t flags; + uint8_t vdd; + uint8_t vcs; + uint32_t freq_khz; } pstates[MAX_PSTATES]; - s8 core_max[MAX_P8_CORES]; - u8 pad[100]; + int8_t core_max[MAX_P8_CORES]; + uint8_t pad[100]; } v2; - struct __packed { /* Version 0x90 */ - u8 occ_role; - u8 pstate_min; - u8 pstate_nom; - u8 pstate_turbo; - u8 pstate_ultra_turbo; - u8 spare; - u64 reserved1; - u64 reserved2; - struct __packed { - u8 id; - u8 flags; - u16 reserved; - __be32 freq_khz; + struct QEMU_PACKED { /* Version 0x90 */ + uint8_t occ_role; + uint8_t pstate_min; + uint8_t pstate_nom; + uint8_t pstate_turbo; + uint8_t pstate_ultra_turbo; + uint8_t spare; + uint64_t reserved1; + uint64_t reserved2; + struct QEMU_PACKED { + uint8_t id; + uint8_t flags; + uint16_t reserved; + uint32_t freq_khz; } pstates[MAX_PSTATES]; - u8 core_max[MAX_P9_CORES]; - u8 pad[56]; + uint8_t core_max[MAX_P9_CORES]; + uint8_t pad[56]; } v9; - struct __packed { /* Version 0xA0 */ - u8 occ_role; - u8 pstate_min; - u8 pstate_fixed_freq; - u8 pstate_base; - u8 pstate_ultra_turbo; - u8 pstate_fmax; - u8 minor; - u8 pstate_bottom_throttle; - u8 spare; - u8 spare1; - u32 reserved_32; - u64 reserved_64; - struct __packed { - u8 id; - u8 valid; - u16 reserved; - __be32 freq_khz; + struct QEMU_PACKED { /* Version 0xA0 */ + uint8_t occ_role; + uint8_t pstate_min; + uint8_t pstate_fixed_freq; + uint8_t pstate_base; + uint8_t pstate_ultra_turbo; + uint8_t pstate_fmax; + uint8_t minor; + uint8_t pstate_bottom_throttle; + uint8_t spare; + uint8_t spare1; + uint32_t reserved_32; + uint64_t reserved_64; + struct QEMU_PACKED { + uint8_t id; + uint8_t valid; + uint16_t reserved; + uint32_t freq_khz; } pstates[MAX_PSTATES]; - u8 core_max[MAX_P10_CORES]; - u8 pad[48]; + uint8_t core_max[MAX_P10_CORES]; + uint8_t pad[48]; } v10; }; -} __packed; +} QEMU_PACKED; /** * OPAL-OCC Command Response Interface @@ -531,13 +522,13 @@ struct occ_pstate_table { * @spare: Unused byte */ struct opal_command_buffer { - u8 flag; - u8 request_id; - u8 cmd; - u8 spare; - __be16 data_size; - u8 data[MAX_OPAL_CMD_DATA_LENGTH]; -} __packed; + uint8_t flag; + uint8_t request_id; + uint8_t cmd; + uint8_t spare; + uint16_t data_size; + uint8_t data[MAX_OPAL_CMD_DATA_LENGTH]; +} QEMU_PACKED; /** * OPAL-OCC Response Buffer @@ -571,13 +562,13 @@ struct opal_command_buffer { * @data: Response specific data */ struct occ_response_buffer { - u8 flag; - u8 request_id; - u8 cmd; - u8 status; - __be16 data_size; - u8 data[MAX_OCC_RSP_DATA_LENGTH]; -} __packed; + uint8_t flag; + uint8_t request_id; + uint8_t cmd; + uint8_t status; + uint16_t data_size; + uint8_t data[MAX_OCC_RSP_DATA_LENGTH]; +} QEMU_PACKED; /** * OCC-OPAL Shared Memory Interface Dynamic Data Vx90 @@ -608,31 +599,31 @@ struct occ_response_buffer { * @rsp: OCC Response Buffer */ struct occ_dynamic_data { - u8 occ_state; - u8 major_version; - u8 minor_version; - u8 gpus_present; - union __packed { - struct __packed { /* Version 0x90 */ - u8 spare1; + uint8_t occ_state; + uint8_t major_version; + uint8_t minor_version; + uint8_t gpus_present; + union QEMU_PACKED { + struct QEMU_PACKED { /* Version 0x90 */ + uint8_t spare1; } v9; - struct __packed { /* Version 0xA0 */ - u8 wof_enabled; + struct QEMU_PACKED { /* Version 0xA0 */ + uint8_t wof_enabled; } v10; }; - u8 cpu_throttle; - u8 mem_throttle; - u8 quick_pwr_drop; - u8 pwr_shifting_ratio; - u8 pwr_cap_type; - __be16 hard_min_pwr_cap; - __be16 max_pwr_cap; - __be16 cur_pwr_cap; - __be16 soft_min_pwr_cap; - u8 pad[110]; + uint8_t cpu_throttle; + uint8_t mem_throttle; + uint8_t quick_pwr_drop; + uint8_t pwr_shifting_ratio; + uint8_t pwr_cap_type; + uint16_t hard_min_pwr_cap; + uint16_t max_pwr_cap; + uint16_t cur_pwr_cap; + uint16_t soft_min_pwr_cap; + uint8_t pad[110]; struct opal_command_buffer cmd; struct occ_response_buffer rsp; -} __packed; +} QEMU_PACKED; enum occ_response_status { OCC_RSP_SUCCESS = 0x00, diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 863e2e7..9db44ca 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -108,6 +108,8 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) memset(s->storage, 0xFF, s->size); } + s->lpc_address = PNOR_SPI_OFFSET; + memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, TYPE_PNV_PNOR, s->size); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a415e51..b0a0f8c 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -296,6 +296,7 @@ static void spapr_dt_pa_features(SpaprMachineState *spapr, pa_features[40 + 2] &= ~0x80; /* Radix MMU */ } if (spapr_get_cap(spapr, SPAPR_CAP_DAWR1)) { + g_assert(pa_size > 66); pa_features[66] |= 0x80; } @@ -4815,6 +4816,7 @@ static void spapr_machine_8_2_class_options(MachineClass *mc) { spapr_machine_9_0_class_options(mc); compat_props_add(mc->compat_props, hw_compat_8_2, hw_compat_8_2_len); + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.2"); } DEFINE_SPAPR_MACHINE(8, 2); diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 0671d9e..faf9170 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -37,6 +37,9 @@ static void spapr_reset_vcpu(PowerPCCPU *cpu) cpu_reset(cs); + env->quiesced = true; /* set "RTAS stopped" state. */ + ppc_maybe_interrupt(env); + /* * "PowerPC Processor binding to IEEE 1275" defines the initial MSR state * as 32bit (MSR_SF=0) with MSR_ME=1 and MSR_FP=1 in "8.2.1. Initial @@ -98,6 +101,9 @@ void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, CPU(cpu)->halted = 0; /* Enable Power-saving mode Exit Cause exceptions */ ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); + + env->quiesced = false; /* clear "RTAS stopped" state. */ + ppc_maybe_interrupt(env); } /* diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 503d441..78309db 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -110,7 +110,8 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, id = rtas_ld(args, 0); cpu = spapr_find_cpu(id); if (cpu != NULL) { - if (CPU(cpu)->halted) { + CPUPPCState *env = &cpu->env; + if (env->quiesced) { rtas_st(rets, 1, 0); } else { rtas_st(rets, 1, 2); @@ -215,6 +216,8 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, * For the same reason, set PSSCR_EC. */ env->spr[SPR_PSSCR] |= PSSCR_EC; + env->quiesced = true; /* set "RTAS stopped" state. */ + ppc_maybe_interrupt(env); cs->halted = 1; ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); kvmppc_set_reg_ppc_online(cpu, 0); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index dae46f4..e517002 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -57,6 +57,7 @@ #include "hw/acpi/aml-build.h" #include "qapi/qapi-visit-common.h" #include "hw/virtio/virtio-iommu.h" +#include "hw/uefi/var-service-api.h" /* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */ static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type) @@ -1935,6 +1936,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) hc->plug = virt_machine_device_plug_cb; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index a48d362..5aaafb4 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -233,20 +233,13 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, NULL, NULL, NULL, NULL, ELFDATA2MSB, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + RAM_size - KERNEL_LOAD_ADDR, true, TARGET_PAGE_SIZE); if (kernel_size < 0) kernel_size = load_image_targphys(kernel_filename, diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 8ab5cf0..d3cb727 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -168,13 +168,6 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { - int bswap_needed; - -#ifdef BSWAP_NEEDED - bswap_needed = 1; -#else - bswap_needed = 0; -#endif kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, kernel_entry, kernel_addr, &kernel_top, NULL, ELFDATA2MSB, EM_SPARCV9, 0, 0); @@ -182,7 +175,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, *kernel_addr = KERNEL_LOAD_ADDR; *kernel_entry = KERNEL_LOAD_ADDR; kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + RAM_size - KERNEL_LOAD_ADDR, true, TARGET_PAGE_SIZE); } if (kernel_size < 0) { diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig index 9ac0084..b3d823c 100644 --- a/hw/timer/Kconfig +++ b/hw/timer/Kconfig @@ -11,7 +11,13 @@ config A9_GTIMER config HPET bool - default y if PC && !HAVE_RUST + default y if PC + # The HPET has both a Rust and a C implementation + select HPET_C if !HAVE_RUST + select X_HPET_RUST if HAVE_RUST + +config HPET_C + bool config I8254 bool diff --git a/hw/timer/meson.build b/hw/timer/meson.build index f5f9eed..178321c 100644 --- a/hw/timer/meson.build +++ b/hw/timer/meson.build @@ -13,7 +13,7 @@ system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-timer.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_mct.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.c')) system_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_gptimer.c')) -system_ss.add(when: 'CONFIG_HPET', if_true: files('hpet.c')) +system_ss.add(when: 'CONFIG_HPET_C', if_true: files('hpet.c')) system_ss.add(when: 'CONFIG_I8254', if_true: files('i8254_common.c', 'i8254.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_epit.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c')) diff --git a/hw/uefi/Kconfig b/hw/uefi/Kconfig index ca6c2bc..046d553 100644 --- a/hw/uefi/Kconfig +++ b/hw/uefi/Kconfig @@ -1,3 +1,3 @@ config UEFI_VARS bool - default y if X86_64 || AARCH64 + default y if X86_64 || AARCH64 || RISCV64 || LOONGARCH64 diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index 8ed8378..4836a0c 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -29,6 +29,7 @@ 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); uv->buffer = g_malloc(uv->buf_size); return 0; } diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c index 761082c..ad3462c 100644 --- a/hw/uefi/var-service-json.c +++ b/hw/uefi/var-service-json.c @@ -178,7 +178,7 @@ void uefi_vars_json_init(uefi_vars_state *uv, Error **errp) void uefi_vars_json_save(uefi_vars_state *uv) { - GString *gstr; + g_autoptr(GString) gstr = NULL; int rc; if (uv->jsonfd == -1) { @@ -187,18 +187,25 @@ void uefi_vars_json_save(uefi_vars_state *uv) gstr = uefi_vars_to_json(uv); - lseek(uv->jsonfd, 0, SEEK_SET); + rc = lseek(uv->jsonfd, 0, SEEK_SET); + if (rc < 0) { + warn_report("%s: lseek error", __func__); + return; + } + rc = ftruncate(uv->jsonfd, 0); if (rc != 0) { warn_report("%s: ftruncate error", __func__); + return; } + rc = write(uv->jsonfd, gstr->str, gstr->len); if (rc != gstr->len) { warn_report("%s: write error", __func__); + return; } - fsync(uv->jsonfd); - g_string_free(gstr, true); + fsync(uv->jsonfd); } void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) @@ -207,7 +214,7 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) QObject *qobj; Visitor *v; char *str; - size_t len; + ssize_t len; int rc; if (uv->jsonfd == -1) { @@ -215,7 +222,12 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) } len = lseek(uv->jsonfd, 0, SEEK_END); + if (len < 0) { + warn_report("%s: lseek error", __func__); + return; + } if (len == 0) { + /* empty file */ return; } @@ -224,6 +236,8 @@ void uefi_vars_json_load(uefi_vars_state *uv, Error **errp) rc = read(uv->jsonfd, str, len); if (rc != len) { warn_report("%s: read error", __func__); + g_free(str); + return; } str[len] = 0; diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index 8260f1e..f808a01 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -353,10 +353,10 @@ static void xen_bus_realize(BusState *bus, Error **errp) xs_node_watch(xenbus->xsh, node, key, xen_bus_backend_changed, xenbus, &local_err); if (local_err) { - /* This need not be treated as a hard error so don't propagate */ - error_reportf_err(local_err, - "failed to set up '%s' enumeration watch: ", - type[i]); + warn_reportf_err(local_err, + "failed to set up '%s' enumeration watch: ", + type[i]); + local_err = NULL; } g_free(node); diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 8cd6c00..47b1444 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -26,18 +26,6 @@ #include "exec/tswap.h" #include "hw/core/cpu.h" -/* some important defines: - * - * HOST_BIG_ENDIAN : whether the host cpu is big endian and - * otherwise little endian. - * - * TARGET_BIG_ENDIAN : same for the target cpu - */ - -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN -#define BSWAP_NEEDED -#endif - /* Target-endianness CPU memory access functions. These fit into the * {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h. */ diff --git a/include/exec/poison.h b/include/exec/poison.h index 8ed04b3..2c151fd 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -37,7 +37,6 @@ #pragma GCC poison TARGET_NAME #pragma GCC poison TARGET_SUPPORTS_MTTCG #pragma GCC poison TARGET_BIG_ENDIAN -#pragma GCC poison BSWAP_NEEDED #pragma GCC poison TARGET_LONG_BITS #pragma GCC poison TARGET_FMT_lx diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index 4fcaf3d..299ca9b 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -52,6 +52,11 @@ struct PL011State { Clock *clk; bool migrate_clk; const unsigned char *id; + /* + * Since some users embed this struct directly, we must + * ensure that the C struct is at least as big as the Rust one. + */ + uint8_t padding_for_rust[16]; }; DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr); diff --git a/include/hw/loader.h b/include/hw/loader.h index 784a93d..d280dc3 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -190,7 +190,7 @@ ssize_t load_elf(const char *filename, void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp); ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size); + bool big_endian, hwaddr target_page_size); #define LOAD_UIMAGE_LOADADDR_INVALID (-1) diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index 19c2d64..b44cafe 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -28,6 +28,7 @@ struct PnvPnor { BlockBackend *blk; uint8_t *storage; + uint32_t lpc_address; /* Offset within LPC FW space */ int64_t size; MemoryRegion mmio; }; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 15fcec5..2c99856 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -52,6 +52,7 @@ extern const PropertyInfo qdev_prop_bool; extern const PropertyInfo qdev_prop_uint8; extern const PropertyInfo qdev_prop_uint16; extern const PropertyInfo qdev_prop_uint32; +extern const PropertyInfo qdev_prop_usize; extern const PropertyInfo qdev_prop_int32; extern const PropertyInfo qdev_prop_uint64; extern const PropertyInfo qdev_prop_uint64_checkmask; diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8799e4e..fa83d78 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2121,7 +2121,7 @@ static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) memcpy(to, from, n); } -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_ehdr(struct elfhdr *ehdr) { bswap16s(&ehdr->e_type); /* Object file type */ @@ -3143,7 +3143,7 @@ static bool parse_elf_properties(const ImageSource *src, * The contents of a valid PT_GNU_PROPERTY is a sequence of uint32_t. * Swap most of them now, beyond the header and namesz. */ -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN for (int i = 4; i < n / 4; i++) { bswap32s(note.data + i); } @@ -3999,7 +3999,7 @@ struct target_elf_prpsinfo { char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ }; -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_prstatus(struct target_elf_prstatus *prstatus) { prstatus->pr_info.si_signo = tswap32(prstatus->pr_info.si_signo); @@ -4038,7 +4038,7 @@ static void bswap_note(struct elf_note *en) static inline void bswap_prstatus(struct target_elf_prstatus *p) { } static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {} static inline void bswap_note(struct elf_note *en) { } -#endif /* BSWAP_NEEDED */ +#endif /* HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN */ /* * Calculate file (dump) size of given memory region. diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 86d773a..5d22759 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -462,7 +462,7 @@ typedef struct { abi_ulong sig[TARGET_NSIG_WORDS]; } target_sigset_t; -#ifdef BSWAP_NEEDED +#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s) { int i; diff --git a/net/vmnet-common.m b/net/vmnet-common.m index 54d900b..ab33ce2 100644 --- a/net/vmnet-common.m +++ b/net/vmnet-common.m @@ -94,7 +94,7 @@ ssize_t vmnet_receive_common(NetClientState *nc, if_status = vmnet_write(s->vmnet_if, &packet, &pkt_cnt); if (if_status != VMNET_SUCCESS) { - error_report("vmnet: write error: %s\n", + error_report("vmnet: write error: %s", vmnet_status_map_str(if_status)); return -1; } diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index f137b49..bf88e0b 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,7 +2,7 @@ // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, ptr::addr_of_mut}; +use std::{ffi::CStr, mem::size_of, ptr::addr_of_mut}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, @@ -12,6 +12,7 @@ use qemu_api::{ prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, Owned, ParentField}, + static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, vmstate::VMStateDescription, }; @@ -124,6 +125,12 @@ pub struct PL011State { pub migrate_clock: bool, } +// Some C users of this device embed its state struct into their own +// structs, so the size of the Rust version must not be any larger +// than the size of the C one. If this assert triggers you need to +// expand the padding_for_rust[] array in the C PL011State struct. +static_assert!(size_of::<PL011State>() <= size_of::<qemu_api::bindings::PL011State>()); + qom_isa!(PL011State : SysBusDevice, DeviceState, Object); #[repr(C)] diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index 0b2076d..b4d4a7e 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -8,8 +8,12 @@ use std::{ }; use qemu_api::{ - bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, - vmstate_subsections, vmstate_unused, zeroable::Zeroable, + bindings::{qdev_prop_bool, qdev_prop_chr}, + c_str, + prelude::*, + vmstate::VMStateDescription, + vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, + zeroable::Zeroable, }; use crate::device::{PL011Registers, PL011State}; diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig index 42e4213..afd9803 100644 --- a/rust/hw/timer/Kconfig +++ b/rust/hw/timer/Kconfig @@ -1,3 +1,2 @@ config X_HPET_RUST bool - default y if PC && HAVE_RUST diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 20e0afd..3ae3ec2 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -12,7 +12,7 @@ use std::{ use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, - qdev_prop_uint32, qdev_prop_uint8, + qdev_prop_uint32, qdev_prop_usize, }, c_str, cell::{BqlCell, BqlRefCell}, @@ -776,7 +776,7 @@ impl HPETState { let timer_id: usize = ((addr - 0x100) / 0x20) as usize; if timer_id <= self.num_timers.get() { // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) - TimerRegister::try_from(addr) + TimerRegister::try_from(addr & 0x18) .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg)) } else { // TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id) @@ -859,8 +859,8 @@ qemu_api::declare_properties! { c_str!("timers"), HPETState, num_timers, - unsafe { &qdev_prop_uint8 }, - u8, + unsafe { &qdev_prop_usize }, + usize, default = HPET_MIN_TIMERS ), qemu_api::define_property!( diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index a3f226c..858685d 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -58,7 +58,8 @@ rust_qemu_api_objs = static_library( libchardev.extract_all_objects(recursive: false), libcrypto.extract_all_objects(recursive: false), libauthz.extract_all_objects(recursive: false), - libio.extract_all_objects(recursive: false)]) + libio.extract_all_objects(recursive: false), + libmigration.extract_all_objects(recursive: false)]) rust_qemu_api_deps = declare_dependency( dependencies: [ qom_ss.dependencies(), @@ -71,7 +72,7 @@ rust_qemu_api_deps = declare_dependency( test('rust-qemu-api-integration', executable( 'rust-qemu-api-integration', - 'tests/tests.rs', + files('tests/tests.rs', 'tests/vmstate_tests.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index 104dec3..eb12e94 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -91,6 +91,21 @@ macro_rules! assert_field_type { } }; }; + + ($t:ty, $i:tt, $ti:ty, num = $num:ident) => { + const _: () = { + #[allow(unused)] + fn assert_field_type(v: $t) { + fn types_must_be_equal<T, U>(_: T) + where + T: $crate::assertions::EqType<Itself = U>, + { + } + let index: usize = v.$num.try_into().unwrap(); + types_must_be_equal::<_, &$ti>(&v.$i[index]); + } + }; + }; } /// Assert that an expression matches a pattern. This can also be @@ -120,3 +135,25 @@ macro_rules! assert_match { ); }; } + +/// Assert at compile time that an expression is true. This is similar +/// to `const { assert!(...); }` but it works outside functions, as well as +/// on versions of Rust before 1.79. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::static_assert; +/// static_assert!("abc".len() == 3); +/// ``` +/// +/// ```compile_fail +/// # use qemu_api::static_assert; +/// static_assert!("abc".len() == 2); // does not compile +/// ``` +#[macro_export] +macro_rules! static_assert { + ($x:expr) => { + const _: () = assert!($x); + }; +} diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index f0510ae..1b2b12e 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -25,13 +25,11 @@ //! functionality that is missing from `vmstate_of!`. use core::{marker::PhantomData, mem, ptr::NonNull}; +use std::os::raw::{c_int, c_void}; pub use crate::bindings::{VMStateDescription, VMStateField}; use crate::{ - bindings::{self, VMStateFlags}, - prelude::*, - qom::Owned, - zeroable::Zeroable, + bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable, }; /// This macro is used to call a function with a generic argument bound @@ -208,7 +206,7 @@ macro_rules! vmstate_of { .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, offset: $crate::offset_of!($struct_name, $field_name), - $(.num_offset: $crate::offset_of!($struct_name, $num),)? + $(num_offset: $crate::offset_of!($struct_name, $num),)? // The calls to `call_func_with_field!` are the magic that // computes most of the VMStateField from the type of the field. info: $crate::info_enum_to_ref!($crate::call_func_with_field!( @@ -256,6 +254,10 @@ impl VMStateField { if (self.flags.0 & VMStateFlags::VMS_POINTER.0) != 0 { self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_POINTER.0); self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0); + // VMS_ARRAY_OF_POINTER flag stores the size of pointer. + // FIXME: *const, *mut, NonNull and Box<> have the same size as usize. + // Resize if more smart pointers are supported. + self.size = std::mem::size_of::<usize>(); } self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_SINGLE.0); self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0); @@ -271,14 +273,21 @@ impl VMStateField { } #[must_use] - pub const fn with_varray_flag<T: VMState>(mut self, flag: VMStateFlags) -> VMStateField { - assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField { self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0); self.flags = VMStateFlags(self.flags.0 | flag.0); + self.num = 0; // varray uses num_offset instead of num. self } #[must_use] + #[allow(unused_mut)] + pub const fn with_varray_flag(mut self, flag: VMStateFlags) -> VMStateField { + assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0); + self.with_varray_flag_unchecked(flag) + } + + #[must_use] pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField { assert!(num <= 0x7FFF_FFFFu32); self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0); @@ -333,6 +342,7 @@ impl_vmstate_transparent!(std::cell::UnsafeCell<T> where T: VMState); impl_vmstate_transparent!(std::pin::Pin<T> where T: VMState); impl_vmstate_transparent!(crate::cell::BqlCell<T> where T: VMState); impl_vmstate_transparent!(crate::cell::BqlRefCell<T> where T: VMState); +impl_vmstate_transparent!(crate::cell::Opaque<T> where T: VMState); #[macro_export] macro_rules! impl_vmstate_bitsized { @@ -379,7 +389,7 @@ impl_vmstate_scalar!(vmstate_info_uint8, u8, VMS_VARRAY_UINT8); impl_vmstate_scalar!(vmstate_info_uint16, u16, VMS_VARRAY_UINT16); impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32); impl_vmstate_scalar!(vmstate_info_uint64, u64); -impl_vmstate_scalar!(vmstate_info_timer, bindings::QEMUTimer); +impl_vmstate_scalar!(vmstate_info_timer, crate::timer::Timer); // Pointer types using the underlying type's VMState plus VMS_POINTER // Note that references are not supported, though references to cells @@ -440,21 +450,23 @@ macro_rules! vmstate_struct { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, - $(.num_offset: $crate::offset_of!($struct_name, $num),)? + $(num_offset: $crate::offset_of!($struct_name, $num),)? offset: { - $crate::assert_field_type!($struct_name, $field_name, $type); + $crate::assert_field_type!($struct_name, $field_name, $type $(, num = $num)?); $crate::offset_of!($struct_name, $field_name) }, size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, - vmsd: unsafe { $vmsd }, - ..$crate::zeroable::Zeroable::ZERO $( - .with_varray_flag($crate::call_func_with_field!( - $crate::vmstate::vmstate_varray_flag, - $struct_name, - $num)) - $(.with_varray_multiply($factor))?)? - } + vmsd: $vmsd, + ..$crate::zeroable::Zeroable::ZERO + } $(.with_varray_flag_unchecked( + $crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num + ) + ) + $(.with_varray_multiply($factor))?)? }; } @@ -475,7 +487,10 @@ macro_rules! vmstate_clock { $crate::offset_of!($struct_name, $field_name) }, size: ::core::mem::size_of::<*const $crate::qdev::Clock>(), - flags: VMStateFlags(VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_POINTER.0), + flags: $crate::bindings::VMStateFlags( + $crate::bindings::VMStateFlags::VMS_STRUCT.0 + | $crate::bindings::VMStateFlags::VMS_POINTER.0, + ), vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, ..$crate::zeroable::Zeroable::ZERO } @@ -499,6 +514,53 @@ macro_rules! vmstate_fields { }} } +pub extern "C" fn rust_vms_test_field_exists<T, F: for<'a> FnCall<(&'a T, u8), bool>>( + opaque: *mut c_void, + version_id: c_int, +) -> bool { + let owner: &T = unsafe { &*(opaque.cast::<T>()) }; + let version: u8 = version_id.try_into().unwrap(); + // SAFETY: the opaque was passed as a reference to `T`. + F::call((owner, version)) +} + +pub type VMSFieldExistCb = unsafe extern "C" fn( + opaque: *mut std::os::raw::c_void, + version_id: std::os::raw::c_int, +) -> bool; + +#[doc(alias = "VMSTATE_VALIDATE")] +#[macro_export] +macro_rules! vmstate_validate { + ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { + $crate::bindings::VMStateField { + name: ::std::ffi::CStr::as_ptr($test_name), + field_exists: { + const fn test_cb_builder__< + T, + F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>, + >( + _phantom: ::core::marker::PhantomData<F>, + ) -> $crate::vmstate::VMSFieldExistCb { + let _: () = F::ASSERT_IS_SOME; + $crate::vmstate::rust_vms_test_field_exists::<T, F> + } + + const fn phantom__<T>(_: &T) -> ::core::marker::PhantomData<T> { + ::core::marker::PhantomData + } + Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) + }, + flags: $crate::bindings::VMStateFlags( + $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 + | $crate::bindings::VMStateFlags::VMS_ARRAY.0, + ), + num: 0, // 0 elements: no data, only run test_fn callback + ..$crate::zeroable::Zeroable::ZERO + } + }; +} + /// A transparent wrapper type for the `subsections` field of /// [`VMStateDescription`]. /// diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 269122e..99a7aab 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -17,6 +17,8 @@ use qemu_api::{ zeroable::Zeroable, }; +mod vmstate_tests; + // Test that macros can compile. pub static VMSTATE: VMStateDescription = VMStateDescription { name: c_str!("name").as_ptr(), diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs new file mode 100644 index 0000000..b8d8b45 --- /dev/null +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -0,0 +1,477 @@ +// Copyright (C) 2025 Intel Corporation. +// Author(s): Zhao Liu <zhai1.liu@intel.com> +// SPDX-License-Identifier: GPL-2.0-or-later + +use std::{ffi::CStr, mem::size_of, os::raw::c_void, ptr::NonNull, slice}; + +use qemu_api::{ + bindings::{ + vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, + vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, + }, + c_str, + cell::{BqlCell, Opaque}, + impl_vmstate_forward, + vmstate::{VMStateDescription, VMStateField}, + vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate, + zeroable::Zeroable, +}; + +const FOO_ARRAY_MAX: usize = 3; + +// =========================== Test VMSTATE_FOOA =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOA: +// - VMSTATE_U16 +// - VMSTATE_UNUSED +// - VMSTATE_VARRAY_UINT16_UNSAFE +// - VMSTATE_VARRAY_MULTIPLY +#[repr(C)] +#[derive(qemu_api_macros::offsets)] +struct FooA { + arr: [u8; FOO_ARRAY_MAX], + num: u16, + arr_mul: [i8; FOO_ARRAY_MAX], + num_mul: u32, + elem: i8, +} + +static VMSTATE_FOOA: VMStateDescription = VMStateDescription { + name: c_str!("foo_a").as_ptr(), + version_id: 1, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooA, elem), + vmstate_unused!(size_of::<i64>()), + vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), + vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_uint16() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"elem\0" + ); + assert_eq!(foo_fields[0].offset, 16); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_unused() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"unused\0" + ); + assert_eq!(foo_fields[1].offset, 0); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_uint16_unsafe() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_UINT16_UNSAFE) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 4); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, 1); + assert_eq!(foo_fields[2].num, 0); + assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_varray_multiply() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; + + // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to + // VMSTATE_VARRAY_MULTIPLY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_mul\0" + ); + assert_eq!(foo_fields[3].offset, 6); + assert_eq!(foo_fields[3].num_offset, 12); + assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, 1); + assert_eq!(foo_fields[3].num, 16); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOA. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOB =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOB: +// - VMSTATE_BOOL_V +// - VMSTATE_U64 +// - VMSTATE_STRUCT_VARRAY_UINT8 +// - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 +// - VMSTATE_ARRAY +#[repr(C)] +#[derive(qemu_api_macros::offsets)] +struct FooB { + arr_a: [FooA; FOO_ARRAY_MAX], + num_a: u8, + arr_a_mul: [FooA; FOO_ARRAY_MAX], + num_a_mul: u32, + wrap: BqlCell<u64>, + val: bool, + // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. + arr_i64: [i64; FOO_ARRAY_MAX], +} + +static VMSTATE_FOOB: VMStateDescription = VMStateDescription { + name: c_str!("foo_b").as_ptr(), + version_id: 2, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooB, val).with_version_id(2), + vmstate_of!(FooB, wrap), + vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), + vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), + vmstate_of!(FooB, arr_i64), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_bool_v() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"val\0" + ); + assert_eq!(foo_fields[0].offset, 136); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 1); + assert_eq!(foo_fields[0].num, 0); + assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_uint64() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"wrap\0" + ); + assert_eq!(foo_fields[1].offset, 128); + assert_eq!(foo_fields[1].num_offset, 0); + assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); + assert_eq!(foo_fields[1].version_id, 0); + assert_eq!(foo_fields[1].size, 8); + assert_eq!(foo_fields[1].num, 0); + assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); + assert!(foo_fields[1].vmsd.is_null()); + assert!(foo_fields[1].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_a\0" + ); + assert_eq!(foo_fields[2].offset, 0); + assert_eq!(foo_fields[2].num_offset, 60); + assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[2].version_id, 1); + assert_eq!(foo_fields[2].size, 20); + assert_eq!(foo_fields[2].num, 0); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 + ); + assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint32_multiply() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to + // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_a_mul\0" + ); + assert_eq!(foo_fields[3].offset, 64); + assert_eq!(foo_fields[3].num_offset, 124); + assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. + assert_eq!(foo_fields[3].version_id, 2); + assert_eq!(foo_fields[3].size, 20); + assert_eq!(foo_fields[3].num, 32); + assert_eq!( + foo_fields[3].flags.0, + VMStateFlags::VMS_STRUCT.0 + | VMStateFlags::VMS_VARRAY_UINT32.0 + | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 + ); + assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA); + assert!(foo_fields[3].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + + // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to + // VMSTATE_ARRAY) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), + b"arr_i64\0" + ); + assert_eq!(foo_fields[4].offset, 144); + assert_eq!(foo_fields[4].num_offset, 0); + assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); + assert_eq!(foo_fields[4].version_id, 0); + assert_eq!(foo_fields[4].size, 8); + assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); + assert!(foo_fields[4].vmsd.is_null()); + assert!(foo_fields[4].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOB. + assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOC =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOC: +// - VMSTATE_POINTER +// - VMSTATE_ARRAY_OF_POINTER +struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. + +impl_vmstate_forward!(FooCWrapper); + +#[repr(C)] +#[derive(qemu_api_macros::offsets)] +struct FooC { + ptr: *const i32, + ptr_a: NonNull<FooA>, + arr_ptr: [Box<u8>; FOO_ARRAY_MAX], + arr_ptr_wrap: FooCWrapper, +} + +static VMSTATE_FOOC: VMStateDescription = VMStateDescription { + name: c_str!("foo_c").as_ptr(), + version_id: 3, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(FooC, ptr).with_version_id(2), + // FIXME: Currently vmstate_struct doesn't support the pointer to structure. + // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>) + vmstate_unused!(size_of::<NonNull<FooA>>()), + vmstate_of!(FooC, arr_ptr), + vmstate_of!(FooC, arr_ptr_wrap), + }, + ..Zeroable::ZERO +}; + +const PTR_SIZE: usize = size_of::<*mut ()>(); + +#[test] +fn test_vmstate_pointer() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"ptr\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); + assert_eq!(foo_fields[0].version_id, 2); + assert_eq!(foo_fields[0].size, 4); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(foo_fields[0].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"arr_ptr\0" + ); + assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); + assert_eq!(foo_fields[2].num_offset, 0); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[2].version_id, 0); + assert_eq!(foo_fields[2].size, PTR_SIZE); + assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[2].vmsd.is_null()); + assert!(foo_fields[2].field_exists.is_none()); +} + +#[test] +fn test_vmstate_macro_array_of_pointer_wrapped() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; + + // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to + // VMSTATE_ARRAY_OF_POINTER) + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), + b"arr_ptr_wrap\0" + ); + assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); + assert_eq!(foo_fields[3].num_offset, 0); + assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[3].version_id, 0); + assert_eq!(foo_fields[3].size, PTR_SIZE); + assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); + assert_eq!( + foo_fields[2].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 + ); + assert!(foo_fields[3].vmsd.is_null()); + assert!(foo_fields[3].field_exists.is_none()); + + // The last VMStateField in VMSTATE_FOOC. + assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); +} + +// =========================== Test VMSTATE_FOOD =========================== +// Test the use cases of the vmstate macro, corresponding to the following C +// macro variants: +// * VMSTATE_FOOD: +// - VMSTATE_VALIDATE + +// Add more member fields when vmstate_of/vmstate_struct support "test" +// parameter. +struct FooD; + +impl FooD { + fn validate_food_0(&self, _version_id: u8) -> bool { + true + } + + fn validate_food_1(_state: &FooD, _version_id: u8) -> bool { + false + } +} + +fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { + true +} + +static VMSTATE_FOOD: VMStateDescription = VMStateDescription { + name: c_str!("foo_d").as_ptr(), + version_id: 3, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_validate!(FooD, c_str!("foo_d_0"), FooD::validate_food_0), + vmstate_validate!(FooD, c_str!("foo_d_1"), FooD::validate_food_1), + vmstate_validate!(FooD, c_str!("foo_d_2"), validate_food_2), + }, + ..Zeroable::ZERO +}; + +#[test] +fn test_vmstate_validate() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) }; + let mut foo_d = FooD; + let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>(); + + // 1st VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), + b"foo_d_0\0" + ); + assert_eq!(foo_fields[0].offset, 0); + assert_eq!(foo_fields[0].num_offset, 0); + assert!(foo_fields[0].info.is_null()); + assert_eq!(foo_fields[0].version_id, 0); + assert_eq!(foo_fields[0].size, 0); + assert_eq!(foo_fields[0].num, 0); + assert_eq!( + foo_fields[0].flags.0, + VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0 + ); + assert!(foo_fields[0].vmsd.is_null()); + assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) }); + + // 2nd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), + b"foo_d_1\0" + ); + assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) }); + + // 3rd VMStateField in VMSTATE_FOOD + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), + b"foo_d_2\0" + ); + assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) }); + + // The last VMStateField in VMSTATE_FOOD. + assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END); +} diff --git a/rust/wrapper.h b/rust/wrapper.h index d927ad6..d4fec54 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -65,3 +65,4 @@ typedef enum memory_order { #include "exec/memattrs.h" #include "qemu/timer.h" #include "exec/address-spaces.h" +#include "hw/char/pl011.h" diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 1cd959a..9fdc305 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -43,7 +43,7 @@ enum { TLBRET_PE = 7, }; -bool check_ps(CPULoongArchState *ent, int ps); +bool check_ps(CPULoongArchState *ent, uint8_t ps); extern const VMStateDescription vmstate_loongarch_cpu; diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 28735c8..7f63e7c 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1081,7 +1081,6 @@ int kvm_arch_init_vcpu(CPUState *cs) int ret; Error *local_err = NULL; - ret = 0; qemu_add_vm_change_state_handler(kvm_loongarch_vm_stage_change, cs); if (!kvm_get_one_reg(cs, KVM_REG_LOONGARCH_DEBUG_INST, &val)) { @@ -1091,29 +1090,34 @@ int kvm_arch_init_vcpu(CPUState *cs) ret = kvm_cpu_check_lsx(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_lasx(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_lbt(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_pmu(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } ret = kvm_cpu_check_pv_features(cs, &local_err); if (ret < 0) { error_report_err(local_err); + return ret; } - return ret; + return 0; } static bool loongarch_get_lbt(Object *obj, Error **errp) diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 379c71e..6a7a65c 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -115,7 +115,7 @@ target_ulong helper_csrwr_ticlr(CPULoongArchState *env, target_ulong val) target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val) { - int shift, ptbase; + uint8_t shift, ptbase; int64_t old_v = env->CSR_PWCL; /* diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 646dbf5..70d1b5c 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -19,12 +19,12 @@ #include "exec/log.h" #include "cpu-csr.h" -bool check_ps(CPULoongArchState *env, int tlb_ps) +bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) { - if (tlb_ps > 64) { - return false; - } - return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); + if (tlb_ps >= 64) { + return false; + } + return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); } void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, @@ -543,7 +543,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, target_ulong level, uint32_t mem_idx) { CPUState *cs = env_cpu(env); - target_ulong badvaddr, index, phys, ret; + target_ulong badvaddr, index, phys; uint64_t dir_base, dir_width; if (unlikely((level == 0) || (level > 4))) { @@ -571,8 +571,7 @@ target_ulong helper_lddir(CPULoongArchState *env, target_ulong base, get_dir_base_width(env, &dir_base, &dir_width, level); index = (badvaddr >> dir_base) & ((1 << dir_width) - 1); phys = base | index << 3; - ret = ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; - return ret; + return ldq_phys(cs->as, phys) & TARGET_PHYS_MASK; } void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index efab54a..3ee8351 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1356,6 +1356,17 @@ struct CPUArchState { * special way (such as routing some resume causes to 0x100, i.e. sreset). */ bool resume_as_sreset; + + /* + * On powernv, quiesced means the CPU has been stopped using PC direct + * control xscom registers. + * + * On spapr, quiesced means it is in the "RTAS stopped" state. + * + * The core halted/stopped variables aren't sufficient for this, because + * they can be changed with various side-band operations like qmp cont, + * powersave interrupts, etc. + */ bool quiesced; #endif diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 8b590e7..7decc09 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2744,14 +2744,6 @@ static void init_proc_e200(CPUPPCState *env) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); /* TOFIX */ - spr_register(env, SPR_BOOKE_DSRR0, "DSRR0", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register(env, SPR_BOOKE_DSRR1, "DSRR1", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); init_tlbs_emb(env); init_excp_e200(env, 0xFFFF0000UL); diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index 44e19aa..c941c89 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -1951,6 +1951,10 @@ static int ppc_next_unmasked_interrupt(CPUPPCState *env) target_ulong lpcr = env->spr[SPR_LPCR]; bool async_deliver; + if (unlikely(env->quiesced)) { + return 0; + } + #ifdef TARGET_PPC64 switch (env->excp_model) { case POWERPC_EXCP_POWER7: diff --git a/target/ppc/translate/vmx-impl.c.inc b/target/ppc/translate/vmx-impl.c.inc index 70d0ad2..92d6e8c 100644 --- a/target/ppc/translate/vmx-impl.c.inc +++ b/target/ppc/translate/vmx-impl.c.inc @@ -994,8 +994,8 @@ static bool do_vector_rotl_quad(DisasContext *ctx, arg_VX *a, bool mask, { TCGv_i64 ah, al, vrb, n, t0, t1, zero = tcg_constant_i64(0); - REQUIRE_VECTOR(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA310); + REQUIRE_VECTOR(ctx); ah = tcg_temp_new_i64(); al = tcg_temp_new_i64(); diff --git a/target/ppc/translate/vsx-impl.c.inc b/target/ppc/translate/vsx-impl.c.inc index a869f30..00ad57c 100644 --- a/target/ppc/translate/vsx-impl.c.inc +++ b/target/ppc/translate/vsx-impl.c.inc @@ -61,8 +61,8 @@ static bool trans_LXVD2X(DisasContext *ctx, arg_LXVD2X *a) TCGv EA; TCGv_i64 t0; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); @@ -80,8 +80,8 @@ static bool trans_LXVW4X(DisasContext *ctx, arg_LXVW4X *a) TCGv EA; TCGv_i64 xth, xtl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); @@ -113,12 +113,12 @@ static bool trans_LXVWSX(DisasContext *ctx, arg_LXVWSX *a) TCGv EA; TCGv_i32 data; + REQUIRE_INSNS_FLAGS2(ctx, ISA300); if (a->rt < 32) { REQUIRE_VSX(ctx); } else { REQUIRE_VECTOR(ctx); } - REQUIRE_INSNS_FLAGS2(ctx, ISA300); gen_set_access_type(ctx, ACCESS_INT); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); @@ -133,8 +133,8 @@ static bool trans_LXVDSX(DisasContext *ctx, arg_LXVDSX *a) TCGv EA; TCGv_i64 data; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); gen_set_access_type(ctx, ACCESS_INT); EA = do_ea_calc(ctx, a->ra, cpu_gpr[a->rb]); @@ -185,8 +185,8 @@ static bool trans_LXVH8X(DisasContext *ctx, arg_LXVH8X *a) TCGv EA; TCGv_i64 xth, xtl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); xth = tcg_temp_new_i64(); xtl = tcg_temp_new_i64(); @@ -208,8 +208,8 @@ static bool trans_LXVB16X(DisasContext *ctx, arg_LXVB16X *a) TCGv EA; TCGv_i128 data; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); @@ -312,8 +312,8 @@ static bool trans_STXVD2X(DisasContext *ctx, arg_STXVD2X *a) TCGv EA; TCGv_i64 t0; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); @@ -331,8 +331,8 @@ static bool trans_STXVW4X(DisasContext *ctx, arg_STXVW4X *a) TCGv EA; TCGv_i64 xsh, xsl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, VSX); + REQUIRE_VSX(ctx); xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); @@ -364,8 +364,8 @@ static bool trans_STXVH8X(DisasContext *ctx, arg_STXVH8X *a) TCGv EA; TCGv_i64 xsh, xsl; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); xsh = tcg_temp_new_i64(); xsl = tcg_temp_new_i64(); @@ -394,8 +394,8 @@ static bool trans_STXVB16X(DisasContext *ctx, arg_STXVB16X *a) TCGv EA; TCGv_i128 data; - REQUIRE_VSX(ctx); REQUIRE_INSNS_FLAGS2(ctx, ISA300); + REQUIRE_VSX(ctx); data = tcg_temp_new_i128(); gen_set_access_type(ctx, ACCESS_INT); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5a8c1f1..3136d15 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -103,8 +103,7 @@ qtests_i386 = \ config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \ slirp.found() ? ['virtio-net-failover'] : []) + \ (unpack_edk2_blobs and \ - (config_all_devices.has_key('CONFIG_HPET') or \ - config_all_devices.has_key('CONFIG_X_HPET_RUST')) and \ + config_all_devices.has_key('CONFIG_HPET') and \ config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \ qtests_pci + \ qtests_cxl + \ |