mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 00:03:54 -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);
|
||||
}
|
||||
|
||||
#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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
config X_HPET_RUST
|
||||
bool
|
||||
default y if PC && HAVE_RUST
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,13 +273,20 @@ 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);
|
||||
|
@ -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`].
|
||||
///
|
||||
|
|
|
@ -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(),
|
||||
|
|
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 "qemu/timer.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 \
|
||||
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 + \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue