mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-15 22:21:57 -06:00
* exec/cpu-all: remove BSWAP_NEEDED
* pl011: pad C PL011State struct to same size as Rust struct * rust: hpet: fix type of "timers" property * rust: hpet: fix functional tests (and really everything that uses it) * rust: Kconfig: Factor out whether devices are Rust or C * rust: vmstate: Fixes and tests -----BEGIN PGP SIGNATURE----- iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmfdsUsUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroOGpwf/Qk4bAcLX7A1/nOmYT+DtWzZ9V/VS hSOe6BruzW8rzwMyn/d7oR+aUpk3sL+v2iPBWqoZ/wh0w8kcABcUfWsqqGI8ln/K pnTdiC+hra5z0AFH1tmjjtOI50WDOeSjh5SFvoPJtGzhEbo89QvsUWgy98HiHOMm YFPDuhg3Pfd1XDcdoaa85sOHO1vDsj45fCEJhx6Ktib4vOlEm2I4Z9YR/JxNMT33 vy/y09HG4cpc6bWKLPL3nqR9RchUSI+YRDZ8rlaXUowiZzH2K/wi0qJOsvG6oJF5 awni0YWuwyFi16jmUub8NFnWk6NKjbACqw74AwoVPbNbDoCrrogXzIF2Lw== =NzCN -----END PGP SIGNATURE----- Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging * exec/cpu-all: remove BSWAP_NEEDED * pl011: pad C PL011State struct to same size as Rust struct * rust: hpet: fix type of "timers" property * rust: hpet: fix functional tests (and really everything that uses it) * rust: Kconfig: Factor out whether devices are Rust or C * rust: vmstate: Fixes and tests # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmfdsUsUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroOGpwf/Qk4bAcLX7A1/nOmYT+DtWzZ9V/VS # hSOe6BruzW8rzwMyn/d7oR+aUpk3sL+v2iPBWqoZ/wh0w8kcABcUfWsqqGI8ln/K # pnTdiC+hra5z0AFH1tmjjtOI50WDOeSjh5SFvoPJtGzhEbo89QvsUWgy98HiHOMm # YFPDuhg3Pfd1XDcdoaa85sOHO1vDsj45fCEJhx6Ktib4vOlEm2I4Z9YR/JxNMT33 # vy/y09HG4cpc6bWKLPL3nqR9RchUSI+YRDZ8rlaXUowiZzH2K/wi0qJOsvG6oJF5 # awni0YWuwyFi16jmUub8NFnWk6NKjbACqw74AwoVPbNbDoCrrogXzIF2Lw== # =NzCN # -----END PGP SIGNATURE----- # gpg: Signature made Fri 21 Mar 2025 14:34:51 EDT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (24 commits) rust: hpet: fix decoding of timer registers rust/vmstate: Include complete crate path of VMStateFlags in vmstate_clock rust/vmstate: Add unit test for vmstate_validate rust/vmstate: Add unit test for pointer case rust/vmstate: Add unit test for vmstate_{of|struct} macro rust/vmstate: Add unit test for vmstate_of macro rust/vmstate: Support vmstate_validate rust/vmstate: Re-implement VMState trait for timer binding rust/vmstate: Relax array check when build varray in vmstate_struct rust/vmstate: Fix unnecessary VMState bound of with_varray_flag() rust/vmstate: Fix "cannot infer type" error in vmstate_struct rust/vmstate: Fix type check for varray in vmstate_struct rust/vmstate: Fix size field of VMStateField with VMS_ARRAY_OF_POINTER flag rust/vmstate: Fix num field when varray flags are set rust/vmstate: Fix num_offset in vmstate macros rust/vmstate: Remove unnecessary unsafe exec/cpu-all: remove BSWAP_NEEDED load_aout: replace bswap_needed with big_endian rust: pl011: Check size of state struct at compile time hw/char/pl011: Pad PL011State struct to same size as Rust impl ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
71119ed365
33 changed files with 705 additions and 109 deletions
|
@ -44,7 +44,7 @@ static inline void memcpy_fromfs(void *to, const void *from, unsigned long n)
|
||||||
memcpy(to, from, n);
|
memcpy(to, from, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BSWAP_NEEDED
|
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN
|
||||||
static void bswap_ehdr(struct elfhdr *ehdr)
|
static void bswap_ehdr(struct elfhdr *ehdr)
|
||||||
{
|
{
|
||||||
bswap16s(&ehdr->e_type); /* Object file type */
|
bswap16s(&ehdr->e_type); /* Object file type */
|
||||||
|
@ -111,7 +111,7 @@ static void bswap_note(struct elf_note *en)
|
||||||
bswap32s(&en->n_type);
|
bswap32s(&en->n_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* ! BSWAP_NEEDED */
|
#else
|
||||||
|
|
||||||
static void bswap_ehdr(struct elfhdr *ehdr) { }
|
static void bswap_ehdr(struct elfhdr *ehdr) { }
|
||||||
static void bswap_phdr(struct elf_phdr *phdr, int phnum) { }
|
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_sym(struct elf_sym *sym) { }
|
||||||
static void bswap_note(struct elf_note *en) { }
|
static void bswap_note(struct elf_note *en) { }
|
||||||
|
|
||||||
#endif /* ! BSWAP_NEEDED */
|
#endif /* HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN */
|
||||||
|
|
||||||
#include "elfcore.c"
|
#include "elfcore.c"
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#CONFIG_APPLESMC=n
|
#CONFIG_APPLESMC=n
|
||||||
#CONFIG_FDC=n
|
#CONFIG_FDC=n
|
||||||
#CONFIG_HPET=n
|
#CONFIG_HPET=n
|
||||||
#CONFIG_X_HPET_RUST=n
|
|
||||||
#CONFIG_HYPERV=n
|
#CONFIG_HYPERV=n
|
||||||
#CONFIG_ISA_DEBUG=n
|
#CONFIG_ISA_DEBUG=n
|
||||||
#CONFIG_ISA_IPMI_BT=n
|
#CONFIG_ISA_IPMI_BT=n
|
||||||
|
|
|
@ -21,8 +21,7 @@ config ARM_VIRT
|
||||||
select PCI_EXPRESS
|
select PCI_EXPRESS
|
||||||
select PCI_EXPRESS_GENERIC_BRIDGE
|
select PCI_EXPRESS_GENERIC_BRIDGE
|
||||||
select PFLASH_CFI01
|
select PFLASH_CFI01
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL031 # RTC
|
select PL031 # RTC
|
||||||
select PL061 # GPIO
|
select PL061 # GPIO
|
||||||
select GPIO_PWR
|
select GPIO_PWR
|
||||||
|
@ -75,8 +74,7 @@ config HIGHBANK
|
||||||
select AHCI_SYSBUS
|
select AHCI_SYSBUS
|
||||||
select ARM_TIMER # sp804
|
select ARM_TIMER # sp804
|
||||||
select ARM_V7M
|
select ARM_V7M
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL022 # SPI
|
select PL022 # SPI
|
||||||
select PL031 # RTC
|
select PL031 # RTC
|
||||||
select PL061 # GPIO
|
select PL061 # GPIO
|
||||||
|
@ -89,8 +87,7 @@ config INTEGRATOR
|
||||||
depends on TCG && ARM
|
depends on TCG && ARM
|
||||||
select ARM_TIMER
|
select ARM_TIMER
|
||||||
select INTEGRATOR_DEBUG
|
select INTEGRATOR_DEBUG
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL031 # RTC
|
select PL031 # RTC
|
||||||
select PL041 # audio
|
select PL041 # audio
|
||||||
select PL050 # keyboard/mouse
|
select PL050 # keyboard/mouse
|
||||||
|
@ -108,8 +105,7 @@ config MUSCA
|
||||||
default y
|
default y
|
||||||
depends on TCG && ARM
|
depends on TCG && ARM
|
||||||
select ARMSSE
|
select ARMSSE
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL031
|
select PL031
|
||||||
select SPLIT_IRQ
|
select SPLIT_IRQ
|
||||||
select UNIMP
|
select UNIMP
|
||||||
|
@ -173,8 +169,7 @@ config REALVIEW
|
||||||
select WM8750 # audio codec
|
select WM8750 # audio codec
|
||||||
select LSI_SCSI_PCI
|
select LSI_SCSI_PCI
|
||||||
select PCI
|
select PCI
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL031 # RTC
|
select PL031 # RTC
|
||||||
select PL041 # audio codec
|
select PL041 # audio codec
|
||||||
select PL050 # keyboard/mouse
|
select PL050 # keyboard/mouse
|
||||||
|
@ -199,8 +194,7 @@ config SBSA_REF
|
||||||
select PCI_EXPRESS
|
select PCI_EXPRESS
|
||||||
select PCI_EXPRESS_GENERIC_BRIDGE
|
select PCI_EXPRESS_GENERIC_BRIDGE
|
||||||
select PFLASH_CFI01
|
select PFLASH_CFI01
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL031 # RTC
|
select PL031 # RTC
|
||||||
select PL061 # GPIO
|
select PL061 # GPIO
|
||||||
select USB_XHCI_SYSBUS
|
select USB_XHCI_SYSBUS
|
||||||
|
@ -224,8 +218,7 @@ config STELLARIS
|
||||||
select ARM_V7M
|
select ARM_V7M
|
||||||
select CMSDK_APB_WATCHDOG
|
select CMSDK_APB_WATCHDOG
|
||||||
select I2C
|
select I2C
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL022 # SPI
|
select PL022 # SPI
|
||||||
select PL061 # GPIO
|
select PL061 # GPIO
|
||||||
select SSD0303 # OLED display
|
select SSD0303 # OLED display
|
||||||
|
@ -285,8 +278,7 @@ config VEXPRESS
|
||||||
select ARM_TIMER # sp804
|
select ARM_TIMER # sp804
|
||||||
select LAN9118
|
select LAN9118
|
||||||
select PFLASH_CFI01
|
select PFLASH_CFI01
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select PL041 # audio codec
|
select PL041 # audio codec
|
||||||
select PL181 # display
|
select PL181 # display
|
||||||
select REALVIEW
|
select REALVIEW
|
||||||
|
@ -371,8 +363,7 @@ config RASPI
|
||||||
default y
|
default y
|
||||||
depends on TCG && ARM
|
depends on TCG && ARM
|
||||||
select FRAMEBUFFER
|
select FRAMEBUFFER
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select SDHCI
|
select SDHCI
|
||||||
select USB_DWC2
|
select USB_DWC2
|
||||||
select BCM2835_SPI
|
select BCM2835_SPI
|
||||||
|
@ -448,8 +439,7 @@ config XLNX_VERSAL
|
||||||
select ARM_GIC
|
select ARM_GIC
|
||||||
select CPU_CLUSTER
|
select CPU_CLUSTER
|
||||||
select DEVICE_TREE
|
select DEVICE_TREE
|
||||||
select PL011 if !HAVE_RUST # UART
|
select PL011 # UART
|
||||||
select X_PL011_RUST if HAVE_RUST # UART
|
|
||||||
select CADENCE
|
select CADENCE
|
||||||
select VIRTIO_MMIO
|
select VIRTIO_MMIO
|
||||||
select UNIMP
|
select UNIMP
|
||||||
|
|
|
@ -11,6 +11,12 @@ config PARALLEL
|
||||||
|
|
||||||
config PL011
|
config PL011
|
||||||
bool
|
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
|
config SERIAL
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -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_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_NRF51_SOC', if_true: files('nrf51_uart.c'))
|
||||||
system_ss.add(when: 'CONFIG_PARALLEL', if_true: files('parallel.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_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', if_true: files('serial.c'))
|
||||||
system_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c'))
|
system_ss.add(when: 'CONFIG_SERIAL_ISA', if_true: files('serial-isa.c'))
|
||||||
|
|
|
@ -226,7 +226,7 @@ static void bswap_ahdr(struct exec *e)
|
||||||
|
|
||||||
|
|
||||||
ssize_t load_aout(const char *filename, hwaddr addr, int max_sz,
|
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;
|
int fd;
|
||||||
ssize_t size, ret;
|
ssize_t size, ret;
|
||||||
|
@ -241,7 +241,7 @@ ssize_t load_aout(const char *filename, hwaddr addr, int max_sz,
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (bswap_needed) {
|
if (big_endian != HOST_BIG_ENDIAN) {
|
||||||
bswap_ahdr(&e);
|
bswap_ahdr(&e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -442,6 +442,43 @@ const PropertyInfo qdev_prop_uint64_checkmask = {
|
||||||
.set = set_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 --- */
|
/* --- string --- */
|
||||||
|
|
||||||
static void release_string(Object *obj, const char *name, void *opaque)
|
static void release_string(Object *obj, const char *name, void *opaque)
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include CONFIG_DEVICES
|
#include CONFIG_DEVICES
|
||||||
#include "target/i386/cpu.h"
|
#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};
|
struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1704,7 +1704,7 @@ static void pc_machine_initfn(Object *obj)
|
||||||
pcms->sata_enabled = true;
|
pcms->sata_enabled = true;
|
||||||
pcms->i8042_enabled = true;
|
pcms->i8042_enabled = true;
|
||||||
pcms->max_fw_size = 8 * MiB;
|
pcms->max_fw_size = 8 * MiB;
|
||||||
#if defined(CONFIG_HPET) || defined(CONFIG_X_HPET_RUST)
|
#if defined(CONFIG_HPET)
|
||||||
pcms->hpet_enabled = true;
|
pcms->hpet_enabled = true;
|
||||||
#endif
|
#endif
|
||||||
pcms->fd_bootchk = true;
|
pcms->fd_bootchk = true;
|
||||||
|
|
|
@ -197,11 +197,6 @@ static void ppc_core99_init(MachineState *machine)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (machine->kernel_filename) {
|
if (machine->kernel_filename) {
|
||||||
int bswap_needed = 0;
|
|
||||||
|
|
||||||
#ifdef BSWAP_NEEDED
|
|
||||||
bswap_needed = 1;
|
|
||||||
#endif
|
|
||||||
kernel_base = KERNEL_LOAD_ADDR;
|
kernel_base = KERNEL_LOAD_ADDR;
|
||||||
kernel_size = load_elf(machine->kernel_filename, NULL,
|
kernel_size = load_elf(machine->kernel_filename, NULL,
|
||||||
translate_kernel_address, NULL, NULL, NULL,
|
translate_kernel_address, NULL, NULL, NULL,
|
||||||
|
@ -209,7 +204,7 @@ static void ppc_core99_init(MachineState *machine)
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_aout(machine->kernel_filename, kernel_base,
|
kernel_size = load_aout(machine->kernel_filename, kernel_base,
|
||||||
machine->ram_size - kernel_base,
|
machine->ram_size - kernel_base,
|
||||||
bswap_needed, TARGET_PAGE_SIZE);
|
true, TARGET_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_image_targphys(machine->kernel_filename,
|
kernel_size = load_image_targphys(machine->kernel_filename,
|
||||||
|
|
|
@ -153,11 +153,6 @@ static void ppc_heathrow_init(MachineState *machine)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (machine->kernel_filename) {
|
if (machine->kernel_filename) {
|
||||||
int bswap_needed = 0;
|
|
||||||
|
|
||||||
#ifdef BSWAP_NEEDED
|
|
||||||
bswap_needed = 1;
|
|
||||||
#endif
|
|
||||||
kernel_base = KERNEL_LOAD_ADDR;
|
kernel_base = KERNEL_LOAD_ADDR;
|
||||||
kernel_size = load_elf(machine->kernel_filename, NULL,
|
kernel_size = load_elf(machine->kernel_filename, NULL,
|
||||||
translate_kernel_address, NULL, NULL, NULL,
|
translate_kernel_address, NULL, NULL, NULL,
|
||||||
|
@ -165,7 +160,7 @@ static void ppc_heathrow_init(MachineState *machine)
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_aout(machine->kernel_filename, kernel_base,
|
kernel_size = load_aout(machine->kernel_filename, kernel_base,
|
||||||
machine->ram_size - kernel_base,
|
machine->ram_size - kernel_base,
|
||||||
bswap_needed, TARGET_PAGE_SIZE);
|
true, TARGET_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_image_targphys(machine->kernel_filename,
|
kernel_size = load_image_targphys(machine->kernel_filename,
|
||||||
|
|
|
@ -233,20 +233,13 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename,
|
||||||
|
|
||||||
kernel_size = 0;
|
kernel_size = 0;
|
||||||
if (linux_boot) {
|
if (linux_boot) {
|
||||||
int bswap_needed;
|
|
||||||
|
|
||||||
#ifdef BSWAP_NEEDED
|
|
||||||
bswap_needed = 1;
|
|
||||||
#else
|
|
||||||
bswap_needed = 0;
|
|
||||||
#endif
|
|
||||||
kernel_size = load_elf(kernel_filename, NULL,
|
kernel_size = load_elf(kernel_filename, NULL,
|
||||||
translate_kernel_address, NULL,
|
translate_kernel_address, NULL,
|
||||||
NULL, NULL, NULL, NULL,
|
NULL, NULL, NULL, NULL,
|
||||||
ELFDATA2MSB, EM_SPARC, 0, 0);
|
ELFDATA2MSB, EM_SPARC, 0, 0);
|
||||||
if (kernel_size < 0)
|
if (kernel_size < 0)
|
||||||
kernel_size = load_aout(kernel_filename, 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);
|
TARGET_PAGE_SIZE);
|
||||||
if (kernel_size < 0)
|
if (kernel_size < 0)
|
||||||
kernel_size = load_image_targphys(kernel_filename,
|
kernel_size = load_image_targphys(kernel_filename,
|
||||||
|
|
|
@ -168,13 +168,6 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename,
|
||||||
|
|
||||||
kernel_size = 0;
|
kernel_size = 0;
|
||||||
if (linux_boot) {
|
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_size = load_elf(kernel_filename, NULL, NULL, NULL, kernel_entry,
|
||||||
kernel_addr, &kernel_top, NULL,
|
kernel_addr, &kernel_top, NULL,
|
||||||
ELFDATA2MSB, EM_SPARCV9, 0, 0);
|
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_addr = KERNEL_LOAD_ADDR;
|
||||||
*kernel_entry = KERNEL_LOAD_ADDR;
|
*kernel_entry = KERNEL_LOAD_ADDR;
|
||||||
kernel_size = load_aout(kernel_filename, 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);
|
TARGET_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
if (kernel_size < 0) {
|
if (kernel_size < 0) {
|
||||||
|
|
|
@ -11,7 +11,13 @@ config A9_GTIMER
|
||||||
|
|
||||||
config HPET
|
config HPET
|
||||||
bool
|
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
|
config I8254
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -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_mct.c'))
|
||||||
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pwm.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_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_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_epit.c'))
|
||||||
system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c'))
|
system_ss.add(when: 'CONFIG_IMX', if_true: files('imx_gpt.c'))
|
||||||
|
|
|
@ -26,18 +26,6 @@
|
||||||
#include "exec/tswap.h"
|
#include "exec/tswap.h"
|
||||||
#include "hw/core/cpu.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
|
/* Target-endianness CPU memory access functions. These fit into the
|
||||||
* {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h.
|
* {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
#pragma GCC poison TARGET_NAME
|
#pragma GCC poison TARGET_NAME
|
||||||
#pragma GCC poison TARGET_SUPPORTS_MTTCG
|
#pragma GCC poison TARGET_SUPPORTS_MTTCG
|
||||||
#pragma GCC poison TARGET_BIG_ENDIAN
|
#pragma GCC poison TARGET_BIG_ENDIAN
|
||||||
#pragma GCC poison BSWAP_NEEDED
|
|
||||||
|
|
||||||
#pragma GCC poison TARGET_LONG_BITS
|
#pragma GCC poison TARGET_LONG_BITS
|
||||||
#pragma GCC poison TARGET_FMT_lx
|
#pragma GCC poison TARGET_FMT_lx
|
||||||
|
|
|
@ -52,6 +52,11 @@ struct PL011State {
|
||||||
Clock *clk;
|
Clock *clk;
|
||||||
bool migrate_clk;
|
bool migrate_clk;
|
||||||
const unsigned char *id;
|
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);
|
DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr);
|
||||||
|
|
|
@ -190,7 +190,7 @@ ssize_t load_elf(const char *filename,
|
||||||
void load_elf_hdr(const char *filename, void *hdr, bool *is64, Error **errp);
|
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,
|
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)
|
#define LOAD_UIMAGE_LOADADDR_INVALID (-1)
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ extern const PropertyInfo qdev_prop_bool;
|
||||||
extern const PropertyInfo qdev_prop_uint8;
|
extern const PropertyInfo qdev_prop_uint8;
|
||||||
extern const PropertyInfo qdev_prop_uint16;
|
extern const PropertyInfo qdev_prop_uint16;
|
||||||
extern const PropertyInfo qdev_prop_uint32;
|
extern const PropertyInfo qdev_prop_uint32;
|
||||||
|
extern const PropertyInfo qdev_prop_usize;
|
||||||
extern const PropertyInfo qdev_prop_int32;
|
extern const PropertyInfo qdev_prop_int32;
|
||||||
extern const PropertyInfo qdev_prop_uint64;
|
extern const PropertyInfo qdev_prop_uint64;
|
||||||
extern const PropertyInfo qdev_prop_uint64_checkmask;
|
extern const PropertyInfo qdev_prop_uint64_checkmask;
|
||||||
|
|
|
@ -2121,7 +2121,7 @@ static inline void memcpy_fromfs(void * to, const void * from, unsigned long n)
|
||||||
memcpy(to, from, n);
|
memcpy(to, from, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BSWAP_NEEDED
|
#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN
|
||||||
static void bswap_ehdr(struct elfhdr *ehdr)
|
static void bswap_ehdr(struct elfhdr *ehdr)
|
||||||
{
|
{
|
||||||
bswap16s(&ehdr->e_type); /* Object file type */
|
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.
|
* The contents of a valid PT_GNU_PROPERTY is a sequence of uint32_t.
|
||||||
* Swap most of them now, beyond the header and namesz.
|
* 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++) {
|
for (int i = 4; i < n / 4; i++) {
|
||||||
bswap32s(note.data + i);
|
bswap32s(note.data + i);
|
||||||
}
|
}
|
||||||
|
@ -3999,7 +3999,7 @@ struct target_elf_prpsinfo {
|
||||||
char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
|
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)
|
static void bswap_prstatus(struct target_elf_prstatus *prstatus)
|
||||||
{
|
{
|
||||||
prstatus->pr_info.si_signo = tswap32(prstatus->pr_info.si_signo);
|
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_prstatus(struct target_elf_prstatus *p) { }
|
||||||
static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {}
|
static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {}
|
||||||
static inline void bswap_note(struct elf_note *en) { }
|
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.
|
* Calculate file (dump) size of given memory region.
|
||||||
|
|
|
@ -462,7 +462,7 @@ typedef struct {
|
||||||
abi_ulong sig[TARGET_NSIG_WORDS];
|
abi_ulong sig[TARGET_NSIG_WORDS];
|
||||||
} target_sigset_t;
|
} 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)
|
static inline void tswap_sigset(target_sigset_t *d, const target_sigset_t *s)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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::{
|
use qemu_api::{
|
||||||
chardev::{CharBackend, Chardev, Event},
|
chardev::{CharBackend, Chardev, Event},
|
||||||
|
@ -12,6 +12,7 @@ use qemu_api::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
|
||||||
qom::{ObjectImpl, Owned, ParentField},
|
qom::{ObjectImpl, Owned, ParentField},
|
||||||
|
static_assert,
|
||||||
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
sysbus::{SysBusDevice, SysBusDeviceImpl},
|
||||||
vmstate::VMStateDescription,
|
vmstate::VMStateDescription,
|
||||||
};
|
};
|
||||||
|
@ -124,6 +125,12 @@ pub struct PL011State {
|
||||||
pub migrate_clock: bool,
|
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);
|
qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
@ -8,8 +8,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct,
|
bindings::{qdev_prop_bool, qdev_prop_chr},
|
||||||
vmstate_subsections, vmstate_unused, zeroable::Zeroable,
|
c_str,
|
||||||
|
prelude::*,
|
||||||
|
vmstate::VMStateDescription,
|
||||||
|
vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused,
|
||||||
|
zeroable::Zeroable,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::device::{PL011Registers, PL011State};
|
use crate::device::{PL011Registers, PL011State};
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
config X_HPET_RUST
|
config X_HPET_RUST
|
||||||
bool
|
bool
|
||||||
default y if PC && HAVE_RUST
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::{
|
||||||
use qemu_api::{
|
use qemu_api::{
|
||||||
bindings::{
|
bindings::{
|
||||||
address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool,
|
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,
|
c_str,
|
||||||
cell::{BqlCell, BqlRefCell},
|
cell::{BqlCell, BqlRefCell},
|
||||||
|
@ -776,7 +776,7 @@ impl HPETState {
|
||||||
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
|
let timer_id: usize = ((addr - 0x100) / 0x20) as usize;
|
||||||
if timer_id <= self.num_timers.get() {
|
if timer_id <= self.num_timers.get() {
|
||||||
// TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id)
|
// 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))
|
.map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg))
|
||||||
} else {
|
} else {
|
||||||
// TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
|
// TODO: Add trace point - trace_hpet_timer_id_out_of_range(timer_id)
|
||||||
|
@ -859,8 +859,8 @@ qemu_api::declare_properties! {
|
||||||
c_str!("timers"),
|
c_str!("timers"),
|
||||||
HPETState,
|
HPETState,
|
||||||
num_timers,
|
num_timers,
|
||||||
unsafe { &qdev_prop_uint8 },
|
unsafe { &qdev_prop_usize },
|
||||||
u8,
|
usize,
|
||||||
default = HPET_MIN_TIMERS
|
default = HPET_MIN_TIMERS
|
||||||
),
|
),
|
||||||
qemu_api::define_property!(
|
qemu_api::define_property!(
|
||||||
|
|
|
@ -58,7 +58,8 @@ rust_qemu_api_objs = static_library(
|
||||||
libchardev.extract_all_objects(recursive: false),
|
libchardev.extract_all_objects(recursive: false),
|
||||||
libcrypto.extract_all_objects(recursive: false),
|
libcrypto.extract_all_objects(recursive: false),
|
||||||
libauthz.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(
|
rust_qemu_api_deps = declare_dependency(
|
||||||
dependencies: [
|
dependencies: [
|
||||||
qom_ss.dependencies(),
|
qom_ss.dependencies(),
|
||||||
|
@ -71,7 +72,7 @@ rust_qemu_api_deps = declare_dependency(
|
||||||
test('rust-qemu-api-integration',
|
test('rust-qemu-api-integration',
|
||||||
executable(
|
executable(
|
||||||
'rust-qemu-api-integration',
|
'rust-qemu-api-integration',
|
||||||
'tests/tests.rs',
|
files('tests/tests.rs', 'tests/vmstate_tests.rs'),
|
||||||
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
override_options: ['rust_std=2021', 'build.rust_std=2021'],
|
||||||
rust_args: ['--test'],
|
rust_args: ['--test'],
|
||||||
install: false,
|
install: false,
|
||||||
|
|
|
@ -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
|
/// 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -25,13 +25,11 @@
|
||||||
//! functionality that is missing from `vmstate_of!`.
|
//! functionality that is missing from `vmstate_of!`.
|
||||||
|
|
||||||
use core::{marker::PhantomData, mem, ptr::NonNull};
|
use core::{marker::PhantomData, mem, ptr::NonNull};
|
||||||
|
use std::os::raw::{c_int, c_void};
|
||||||
|
|
||||||
pub use crate::bindings::{VMStateDescription, VMStateField};
|
pub use crate::bindings::{VMStateDescription, VMStateField};
|
||||||
use crate::{
|
use crate::{
|
||||||
bindings::{self, VMStateFlags},
|
bindings::VMStateFlags, callbacks::FnCall, prelude::*, qom::Owned, zeroable::Zeroable,
|
||||||
prelude::*,
|
|
||||||
qom::Owned,
|
|
||||||
zeroable::Zeroable,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This macro is used to call a function with a generic argument bound
|
/// This macro is used to call a function with a generic argument bound
|
||||||
|
@ -208,7 +206,7 @@ macro_rules! vmstate_of {
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.as_ptr() as *const ::std::os::raw::c_char,
|
.as_ptr() as *const ::std::os::raw::c_char,
|
||||||
offset: $crate::offset_of!($struct_name, $field_name),
|
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
|
// The calls to `call_func_with_field!` are the magic that
|
||||||
// computes most of the VMStateField from the type of the field.
|
// computes most of the VMStateField from the type of the field.
|
||||||
info: $crate::info_enum_to_ref!($crate::call_func_with_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 {
|
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_POINTER.0);
|
||||||
self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY_OF_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_SINGLE.0);
|
||||||
self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0);
|
self.flags = VMStateFlags(self.flags.0 | VMStateFlags::VMS_ARRAY.0);
|
||||||
|
@ -271,13 +273,20 @@ impl VMStateField {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn with_varray_flag<T: VMState>(mut self, flag: VMStateFlags) -> VMStateField {
|
pub const fn with_varray_flag_unchecked(mut self, flag: VMStateFlags) -> VMStateField {
|
||||||
assert!((self.flags.0 & VMStateFlags::VMS_ARRAY.0) != 0);
|
|
||||||
self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0);
|
self.flags = VMStateFlags(self.flags.0 & !VMStateFlags::VMS_ARRAY.0);
|
||||||
self.flags = VMStateFlags(self.flags.0 | flag.0);
|
self.flags = VMStateFlags(self.flags.0 | flag.0);
|
||||||
|
self.num = 0; // varray uses num_offset instead of num.
|
||||||
self
|
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]
|
#[must_use]
|
||||||
pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField {
|
pub const fn with_varray_multiply(mut self, num: u32) -> VMStateField {
|
||||||
assert!(num <= 0x7FFF_FFFFu32);
|
assert!(num <= 0x7FFF_FFFFu32);
|
||||||
|
@ -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!(std::pin::Pin<T> where T: VMState);
|
||||||
impl_vmstate_transparent!(crate::cell::BqlCell<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::BqlRefCell<T> where T: VMState);
|
||||||
|
impl_vmstate_transparent!(crate::cell::Opaque<T> where T: VMState);
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! impl_vmstate_bitsized {
|
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_uint16, u16, VMS_VARRAY_UINT16);
|
||||||
impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
|
impl_vmstate_scalar!(vmstate_info_uint32, u32, VMS_VARRAY_UINT32);
|
||||||
impl_vmstate_scalar!(vmstate_info_uint64, u64);
|
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
|
// Pointer types using the underlying type's VMState plus VMS_POINTER
|
||||||
// Note that references are not supported, though references to cells
|
// 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")
|
name: ::core::concat!(::core::stringify!($field_name), "\0")
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.as_ptr() as *const ::std::os::raw::c_char,
|
.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: {
|
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)
|
$crate::offset_of!($struct_name, $field_name)
|
||||||
},
|
},
|
||||||
size: ::core::mem::size_of::<$type>(),
|
size: ::core::mem::size_of::<$type>(),
|
||||||
flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
|
flags: $crate::bindings::VMStateFlags::VMS_STRUCT,
|
||||||
vmsd: unsafe { $vmsd },
|
vmsd: $vmsd,
|
||||||
..$crate::zeroable::Zeroable::ZERO $(
|
..$crate::zeroable::Zeroable::ZERO
|
||||||
.with_varray_flag($crate::call_func_with_field!(
|
} $(.with_varray_flag_unchecked(
|
||||||
|
$crate::call_func_with_field!(
|
||||||
$crate::vmstate::vmstate_varray_flag,
|
$crate::vmstate::vmstate_varray_flag,
|
||||||
$struct_name,
|
$struct_name,
|
||||||
$num))
|
$num
|
||||||
|
)
|
||||||
|
)
|
||||||
$(.with_varray_multiply($factor))?)?
|
$(.with_varray_multiply($factor))?)?
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,7 +487,10 @@ macro_rules! vmstate_clock {
|
||||||
$crate::offset_of!($struct_name, $field_name)
|
$crate::offset_of!($struct_name, $field_name)
|
||||||
},
|
},
|
||||||
size: ::core::mem::size_of::<*const $crate::qdev::Clock>(),
|
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) },
|
vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) },
|
||||||
..$crate::zeroable::Zeroable::ZERO
|
..$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
|
/// A transparent wrapper type for the `subsections` field of
|
||||||
/// [`VMStateDescription`].
|
/// [`VMStateDescription`].
|
||||||
///
|
///
|
||||||
|
|
|
@ -17,6 +17,8 @@ use qemu_api::{
|
||||||
zeroable::Zeroable,
|
zeroable::Zeroable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod vmstate_tests;
|
||||||
|
|
||||||
// Test that macros can compile.
|
// Test that macros can compile.
|
||||||
pub static VMSTATE: VMStateDescription = VMStateDescription {
|
pub static VMSTATE: VMStateDescription = VMStateDescription {
|
||||||
name: c_str!("name").as_ptr(),
|
name: c_str!("name").as_ptr(),
|
||||||
|
|
477
rust/qemu-api/tests/vmstate_tests.rs
Normal file
477
rust/qemu-api/tests/vmstate_tests.rs
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -65,3 +65,4 @@ typedef enum memory_order {
|
||||||
#include "exec/memattrs.h"
|
#include "exec/memattrs.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
|
#include "hw/char/pl011.h"
|
||||||
|
|
|
@ -103,8 +103,7 @@ qtests_i386 = \
|
||||||
config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \
|
config_all_devices.has_key('CONFIG_VIRTIO_PCI') and \
|
||||||
slirp.found() ? ['virtio-net-failover'] : []) + \
|
slirp.found() ? ['virtio-net-failover'] : []) + \
|
||||||
(unpack_edk2_blobs and \
|
(unpack_edk2_blobs and \
|
||||||
(config_all_devices.has_key('CONFIG_HPET') or \
|
config_all_devices.has_key('CONFIG_HPET') and \
|
||||||
config_all_devices.has_key('CONFIG_X_HPET_RUST')) and \
|
|
||||||
config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \
|
config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \
|
||||||
qtests_pci + \
|
qtests_pci + \
|
||||||
qtests_cxl + \
|
qtests_cxl + \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue