diff options
159 files changed, 4968 insertions, 1605 deletions
diff --git a/.gitlab-ci.d/check-dco.py b/.gitlab-ci.d/check-dco.py index 70dec7d6ee..2fd56683dc 100755 --- a/.gitlab-ci.d/check-dco.py +++ b/.gitlab-ci.d/check-dco.py @@ -21,7 +21,7 @@ repourl = "https://gitlab.com/%s/%s.git" % (namespace, reponame) print(f"adding upstream git repo @ {repourl}") subprocess.check_call(["git", "remote", "add", "check-dco", repourl]) -subprocess.check_call(["git", "fetch", "check-dco", "master"]) +subprocess.check_call(["git", "fetch", "--refetch", "check-dco", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-dco/master", "HEAD"], diff --git a/.gitlab-ci.d/check-patch.py b/.gitlab-ci.d/check-patch.py index 68c549a146..be13e6f77d 100755 --- a/.gitlab-ci.d/check-patch.py +++ b/.gitlab-ci.d/check-patch.py @@ -24,7 +24,7 @@ print(f"adding upstream git repo @ {repourl}") # base for the user's branch. We thus need to figure out a common # ancestor between the user's branch and current git master. subprocess.check_call(["git", "remote", "add", "check-patch", repourl]) -subprocess.check_call(["git", "fetch", "check-patch", "master"]) +subprocess.check_call(["git", "fetch", "--refetch", "check-patch", "master"]) ancestor = subprocess.check_output(["git", "merge-base", "check-patch/master", "HEAD"], diff --git a/Kconfig.host b/Kconfig.host index 842cbe0d6c..933425c74b 100644 --- a/Kconfig.host +++ b/Kconfig.host @@ -61,3 +61,6 @@ config HV_BALLOON_POSSIBLE config HAVE_RUST bool + +config MAC_PVG + bool diff --git a/MAINTAINERS b/MAINTAINERS index 1911949526..2e7fc6fa91 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -820,6 +820,19 @@ F: hw/pci-host/designware.c F: include/hw/pci-host/designware.h F: docs/system/arm/mcimx7d-sabre.rst +MCIMX8MP-EVK / i.MX8MP +M: Bernhard Beschow <shentey@gmail.com> +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/imx8mp-evk.c +F: hw/arm/fsl-imx8mp.c +F: hw/misc/imx8mp_*.c +F: hw/pci-host/fsl_imx8m_phy.c +F: include/hw/arm/fsl-imx8mp.h +F: include/hw/misc/imx8mp_*.h +F: include/hw/pci-host/fsl_imx8m_phy.h +F: docs/system/arm/imx8mp-evk.rst + MPS2 / MPS3 M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 90958e5a30..5d8aa3a45b 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -139,16 +139,22 @@ anymore. Writing Rust code in QEMU ------------------------- -Right now QEMU includes three crates: +QEMU includes four crates: * ``qemu_api`` for bindings to C code and useful functionality * ``qemu_api_macros`` defines several procedural macros that are useful when writing C code -* ``pl011`` (under ``rust/hw/char/pl011``) is the sample device that is being - used to further develop ``qemu_api`` and ``qemu_api_macros``. It is a functional - replacement for the ``hw/char/pl011.c`` file. +* ``pl011`` (under ``rust/hw/char/pl011``) and ``hpet`` (under ``rust/hw/timer/hpet``) + are sample devices that demonstrate ``qemu_api`` and ``qemu_api_macros``, and are + used to further develop them. These two crates are functional\ [#issues]_ replacements + for the ``hw/char/pl011.c`` and ``hw/timer/hpet.c`` files. + +.. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c`` + as of commit 02b1f7f61928. The ``hpet`` crate is synchronized as of + commit f32352ff9e. Both are lacking tracing functionality; ``hpet`` + is also lacking support for migration. This section explains how to work with them. @@ -179,6 +185,7 @@ module status ``callbacks`` complete ``cell`` stable ``c_str`` complete +``errno`` complete ``irq`` complete ``memory`` stable ``module`` complete @@ -293,7 +300,7 @@ to a Rust mutable reference, and use a shared reference instead. Rust code will then have to use QEMU's ``BqlRefCell`` and ``BqlCell`` type, which enforce that locking rules for the "Big QEMU Lock" are respected. These cell types are also known to the ``vmstate`` crate, which is able to "look inside" -them when building an in-memory representation of a ``struct``s layout. +them when building an in-memory representation of a ``struct``'s layout. Note that the same is not true of a ``RefCell`` or ``Mutex``. In the future, similar cell types might also be provided for ``AioContext``-based @@ -349,7 +356,7 @@ Writing procedural macros ''''''''''''''''''''''''' By conventions, procedural macros are split in two functions, one -returning ``Result<proc_macro2::TokenStream, MacroError>` with the body of +returning ``Result<proc_macro2::TokenStream, MacroError>`` with the body of the procedural macro, and the second returning ``proc_macro::TokenStream`` which is the actual procedural macro. The former's name is the same as the latter with the ``_or_error`` suffix. The code for the latter is more diff --git a/docs/system/arm/imx8mp-evk.rst b/docs/system/arm/imx8mp-evk.rst new file mode 100644 index 0000000000..00527b0cbe --- /dev/null +++ b/docs/system/arm/imx8mp-evk.rst @@ -0,0 +1,70 @@ +NXP i.MX 8M Plus Evaluation Kit (``imx8mp-evk``) +================================================ + +The ``imx8mp-evk`` machine models the i.MX 8M Plus Evaluation Kit, based on an +i.MX 8M Plus SoC. + +Supported devices +----------------- + +The ``imx8mp-evk`` machine implements the following devices: + + * Up to 4 Cortex-A53 cores + * Generic Interrupt Controller (GICv3) + * 4 UARTs + * 3 USDHC Storage Controllers + * 1 Designware PCI Express Controller + * 1 Ethernet Controller + * 2 Designware USB 3 Controllers + * 5 GPIO Controllers + * 6 I2C Controllers + * 3 SPI Controllers + * 3 Watchdogs + * 6 General Purpose Timers + * Secure Non-Volatile Storage (SNVS) including an RTC + * Clock Tree + +Boot options +------------ + +The ``imx8mp-evk`` machine can start a Linux kernel directly using the standard +``-kernel`` functionality. + +Direct Linux Kernel Boot +'''''''''''''''''''''''' + +Probably the easiest way to get started with a whole Linux system on the machine +is to generate an image with Buildroot. Version 2024.11.1 is tested at the time +of writing and involves three steps. First run the following commands in the +toplevel directory of the Buildroot source tree: + +.. code-block:: bash + + $ make freescale_imx8mpevk_defconfig + $ make + +Once finished successfully there is an ``output/image`` subfolder. Navigate into +it and resize the SD card image to a power of two: + +.. code-block:: bash + + $ qemu-img resize sdcard.img 256M + +Finally, the device tree needs to be patched with the following commands which +will remove the ``cpu-idle-states`` properties from CPU nodes: + +.. code-block:: bash + + $ dtc imx8mp-evk.dtb | sed '/cpu-idle-states/d' > imx8mp-evk-patched.dts + $ dtc imx8mp-evk-patched.dts -o imx8mp-evk-patched.dtb + +Now that everything is prepared the machine can be started as follows: + +.. code-block:: bash + + $ qemu-system-aarch64 -M imx8mp-evk -smp 4 -m 3G \ + -display none -serial null -serial stdio \ + -kernel Image \ + -dtb imx8mp-evk-patched.dtb \ + -append "root=/dev/mmcblk2p2" \ + -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2 diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 0c9c2ce035..adf446c0a2 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -144,6 +144,10 @@ highmem-mmio Set ``on``/``off`` to enable/disable the high memory region for PCI MMIO. The default is ``on``. +highmem-mmio-size + Set the high memory region size for PCI MMIO. Must be a power of 2 and + greater than or equal to the default size (512G). + gic-version Specify the version of the Generic Interrupt Controller (GIC) to provide. Valid values are: diff --git a/docs/system/devices/nvme.rst b/docs/system/devices/nvme.rst index d2b1ca9645..6509b35fcb 100644 --- a/docs/system/devices/nvme.rst +++ b/docs/system/devices/nvme.rst @@ -53,6 +53,13 @@ parameters. Vendor ID. Set this to ``on`` to revert to the unallocated Intel ID previously used. +``ocp`` (default: ``off``) + The Open Compute Project defines the Datacenter NVMe SSD Specification that + sits on top of NVMe. It describes additional commands and NVMe behaviors + specific for the Datacenter. When this option is ``on`` OCP features such as + the SMART / Health information extended log become available in the + controller. We emulate version 5 of this log page. + Additional Namespaces --------------------- diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index 9aaa9c414c..a43ec8f10e 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -95,6 +95,7 @@ Board-specific documentation arm/imx25-pdk arm/mcimx6ul-evk arm/mcimx7d-sabre + arm/imx8mp-evk arm/orangepi arm/raspi arm/collie diff --git a/fpu/meson.build b/fpu/meson.build index 1a9992ded5..646c76f0c6 100644 --- a/fpu/meson.build +++ b/fpu/meson.build @@ -1 +1 @@ -specific_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c')) +common_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c')) diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 1d09f066c5..171bfd06e3 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -195,6 +195,25 @@ static FloatPartsN *partsN(pick_nan_muladd)(FloatPartsN *a, FloatPartsN *b, static void partsN(canonicalize)(FloatPartsN *p, float_status *status, const FloatFmt *fmt) { + /* + * It's target-dependent how to handle the case of exponent 0 + * and Integer bit set. Intel calls these "pseudodenormals", + * and treats them as if the integer bit was 0, and never + * produces them on output. This is the default behaviour for QEMU. + * For m68k, the integer bit is considered validly part of the + * input value when the exponent is 0, and may be 0 or 1, + * giving extra range. They may also be generated as outputs. + * (The m68k manual actually calls these values part of the + * normalized number range, not the denormalized number range, + * but that distinction is not important for us, because + * m68k doesn't care about the input_denormal_used status flag.) + * floatx80_pseudo_denormal_valid selects the m68k behaviour, + * which changes both how we canonicalize such a value and + * how we uncanonicalize results. + */ + bool has_pseudo_denormals = fmt->has_explicit_bit && + (status->floatx80_behaviour & floatx80_pseudo_denormal_valid); + if (unlikely(p->exp == 0)) { if (likely(frac_eqz(p))) { p->cls = float_class_zero; @@ -206,7 +225,7 @@ static void partsN(canonicalize)(FloatPartsN *p, float_status *status, int shift = frac_normalize(p); p->cls = float_class_denormal; p->exp = fmt->frac_shift - fmt->exp_bias - - shift + !fmt->m68k_denormal; + - shift + !has_pseudo_denormals; } } else if (likely(p->exp < fmt->exp_max) || fmt->arm_althp) { p->cls = float_class_normal; @@ -342,13 +361,15 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, frac_clear(p); } else { bool is_tiny = s->tininess_before_rounding || exp < 0; + bool has_pseudo_denormals = fmt->has_explicit_bit && + (s->floatx80_behaviour & floatx80_pseudo_denormal_valid); if (!is_tiny) { FloatPartsN discard; is_tiny = !frac_addi(&discard, p, inc); } - frac_shrjam(p, !fmt->m68k_denormal - exp); + frac_shrjam(p, !has_pseudo_denormals - exp); if (p->frac_lo & round_mask) { /* Need to recompute round-to-even/round-to-odd. */ @@ -379,7 +400,7 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, p->frac_lo &= ~round_mask; } - exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !fmt->m68k_denormal; + exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) && !has_pseudo_denormals; frac_shr(p, frac_shift); if (is_tiny) { diff --git a/fpu/softfloat-specialize.c.inc b/fpu/softfloat-specialize.c.inc index cbbbab52ba..ba4fa08b7b 100644 --- a/fpu/softfloat-specialize.c.inc +++ b/fpu/softfloat-specialize.c.inc @@ -85,11 +85,7 @@ this code that are retained. */ static inline bool no_signaling_nans(float_status *status) { -#if defined(TARGET_XTENSA) return status->no_signaling_nans; -#else - return false; -#endif } /* Define how the architecture discriminates signaling NaNs. @@ -97,17 +93,10 @@ static inline bool no_signaling_nans(float_status *status) * In IEEE 754-1985 this was implementation defined, but in IEEE 754-2008 * the msb must be zero. MIPS is (so far) unique in supporting both the * 2008 revision and backward compatibility with their original choice. - * Thus for MIPS we must make the choice at runtime. */ static inline bool snan_bit_is_one(float_status *status) { -#if defined(TARGET_MIPS) return status->snan_bit_is_one; -#elif defined(TARGET_HPPA) || defined(TARGET_SH4) - return 1; -#else - return 0; -#endif } /*---------------------------------------------------------------------------- @@ -227,15 +216,15 @@ floatx80 floatx80_default_nan(float_status *status) | The pattern for a default generated extended double-precision inf. *----------------------------------------------------------------------------*/ -#define floatx80_infinity_high 0x7FFF -#if defined(TARGET_M68K) -#define floatx80_infinity_low UINT64_C(0x0000000000000000) -#else -#define floatx80_infinity_low UINT64_C(0x8000000000000000) -#endif - -const floatx80 floatx80_infinity - = make_floatx80_init(floatx80_infinity_high, floatx80_infinity_low); +floatx80 floatx80_default_inf(bool zSign, float_status *status) +{ + /* + * Whether the Integer bit is set in the default Infinity is + * target dependent. + */ + bool z = status->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero; + return packFloatx80(zSign, 0x7fff, z ? 0 : (1ULL << 63)); +} /*---------------------------------------------------------------------------- | Returns 1 if the half-precision floating-point value `a' is a quiet diff --git a/fpu/softfloat.c b/fpu/softfloat.c index f4fed9bfda..34c962d6bd 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -79,9 +79,6 @@ this code that are retained. * version 2 or later. See the COPYING file in the top-level directory. */ -/* softfloat (and in particular the code in softfloat-specialize.h) is - * target-dependent and needs the TARGET_* macros. - */ #include "qemu/osdep.h" #include <math.h> #include "qemu/bitops.h" @@ -220,11 +217,9 @@ GEN_INPUT_FLUSH3(float64_input_flush3, float64) * the use of hardfloat, since hardfloat relies on the inexact flag being * already set. */ -#if defined(TARGET_PPC) || defined(__FAST_MATH__) # if defined(__FAST_MATH__) # warning disabling hardfloat due to -ffast-math: hardfloat requires an exact \ IEEE implementation -# endif # define QEMU_NO_HARDFLOAT 1 # define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN #else @@ -537,7 +532,8 @@ typedef struct { * round_mask: bits below lsb which must be rounded * The following optional modifiers are available: * arm_althp: handle ARM Alternative Half Precision - * m68k_denormal: explicit integer bit for extended precision may be 1 + * has_explicit_bit: has an explicit integer bit; this affects whether + * the float_status floatx80_behaviour handling applies */ typedef struct { int exp_size; @@ -547,7 +543,7 @@ typedef struct { int frac_size; int frac_shift; bool arm_althp; - bool m68k_denormal; + bool has_explicit_bit; uint64_t round_mask; } FloatFmt; @@ -600,9 +596,7 @@ static const FloatFmt floatx80_params[3] = { [floatx80_precision_d] = { FLOATX80_PARAMS(52) }, [floatx80_precision_x] = { FLOATX80_PARAMS(64), -#ifdef TARGET_M68K - .m68k_denormal = true, -#endif + .has_explicit_bit = true, }, }; @@ -1810,7 +1804,7 @@ static bool floatx80_unpack_canonical(FloatParts128 *p, floatx80 f, g_assert_not_reached(); } - if (unlikely(floatx80_invalid_encoding(f))) { + if (unlikely(floatx80_invalid_encoding(f, s))) { float_raise(float_flag_invalid, s); return false; } @@ -1860,7 +1854,8 @@ static floatx80 floatx80_round_pack_canonical(FloatParts128 *p, case float_class_inf: /* x86 and m68k differ in the setting of the integer bit. */ - frac = floatx80_infinity_low; + frac = s->floatx80_behaviour & floatx80_default_inf_int_bit_is_zero ? + 0 : (1ULL << 63); exp = fmt->exp_max; break; @@ -5144,9 +5139,7 @@ floatx80 roundAndPackFloatx80(FloatX80RoundPrec roundingPrecision, bool zSign, ) { return packFloatx80( zSign, 0x7FFE, ~ roundMask ); } - return packFloatx80(zSign, - floatx80_infinity_high, - floatx80_infinity_low); + return floatx80_default_inf(zSign, status); } if ( zExp <= 0 ) { isTiny = status->tininess_before_rounding diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 504841ccab..faa00d1db3 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -593,6 +593,30 @@ config FSL_IMX7 select UNIMP select USB_CHIPIDEA +config FSL_IMX8MP + bool + imply I2C_DEVICES + imply PCI_DEVICES + select ARM_GIC + select FSL_IMX8MP_ANALOG + select FSL_IMX8MP_CCM + select IMX + select IMX_FEC + select IMX_I2C + select OR_IRQ + select PCI_EXPRESS_DESIGNWARE + select PCI_EXPRESS_FSL_IMX8M_PHY + select SDHCI + select UNIMP + select USB_DWC3 + select WDT_IMX2 + +config FSL_IMX8MP_EVK + bool + default y + depends on TCG && AARCH64 + select FSL_IMX8MP + config ARM_SMMUV3 bool diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 42c18355e8..e296b62fa1 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -661,8 +661,6 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, binfo->modify_dtb(binfo, fdt); } - qemu_fdt_dumpdtb(fdt, size); - /* Put the DTB into the memory map as a ROM image: this will ensure * the DTB is copied again upon reset, even if addr points into RAM. */ diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c new file mode 100644 index 0000000000..1ea98e1463 --- /dev/null +++ b/hw/arm/fsl-imx8mp.c @@ -0,0 +1,714 @@ +/* + * i.MX 8M Plus SoC Implementation + * + * Based on hw/arm/fsl-imx6.c + * + * Copyright (c) 2024, Bernhard Beschow <shentey@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/bsa.h" +#include "hw/arm/fsl-imx8mp.h" +#include "hw/intc/arm_gicv3.h" +#include "hw/misc/unimp.h" +#include "hw/boards.h" +#include "system/system.h" +#include "target/arm/cpu-qom.h" +#include "qapi/error.h" +#include "qobject/qlist.h" + +static const struct { + hwaddr addr; + size_t size; + const char *name; +} fsl_imx8mp_memmap[] = { + [FSL_IMX8MP_RAM] = { FSL_IMX8MP_RAM_START, FSL_IMX8MP_RAM_SIZE_MAX, "ram" }, + [FSL_IMX8MP_DDR_PHY_BROADCAST] = { 0x3dc00000, 4 * MiB, "ddr_phy_broadcast" }, + [FSL_IMX8MP_DDR_PERF_MON] = { 0x3d800000, 4 * MiB, "ddr_perf_mon" }, + [FSL_IMX8MP_DDR_CTL] = { 0x3d400000, 4 * MiB, "ddr_ctl" }, + [FSL_IMX8MP_DDR_BLK_CTRL] = { 0x3d000000, 1 * MiB, "ddr_blk_ctrl" }, + [FSL_IMX8MP_DDR_PHY] = { 0x3c000000, 16 * MiB, "ddr_phy" }, + [FSL_IMX8MP_AUDIO_DSP] = { 0x3b000000, 16 * MiB, "audio_dsp" }, + [FSL_IMX8MP_GIC_DIST] = { 0x38800000, 512 * KiB, "gic_dist" }, + [FSL_IMX8MP_GIC_REDIST] = { 0x38880000, 512 * KiB, "gic_redist" }, + [FSL_IMX8MP_NPU] = { 0x38500000, 2 * MiB, "npu" }, + [FSL_IMX8MP_VPU] = { 0x38340000, 2 * MiB, "vpu" }, + [FSL_IMX8MP_VPU_BLK_CTRL] = { 0x38330000, 2 * MiB, "vpu_blk_ctrl" }, + [FSL_IMX8MP_VPU_VC8000E_ENCODER] = { 0x38320000, 2 * MiB, "vpu_vc8000e_encoder" }, + [FSL_IMX8MP_VPU_G2_DECODER] = { 0x38310000, 2 * MiB, "vpu_g2_decoder" }, + [FSL_IMX8MP_VPU_G1_DECODER] = { 0x38300000, 2 * MiB, "vpu_g1_decoder" }, + [FSL_IMX8MP_USB2_GLUE] = { 0x382f0000, 0x100, "usb2_glue" }, + [FSL_IMX8MP_USB2_OTG] = { 0x3820cc00, 0x100, "usb2_otg" }, + [FSL_IMX8MP_USB2_DEV] = { 0x3820c700, 0x500, "usb2_dev" }, + [FSL_IMX8MP_USB2] = { 0x38200000, 0xc700, "usb2" }, + [FSL_IMX8MP_USB1_GLUE] = { 0x381f0000, 0x100, "usb1_glue" }, + [FSL_IMX8MP_USB1_OTG] = { 0x3810cc00, 0x100, "usb1_otg" }, + [FSL_IMX8MP_USB1_DEV] = { 0x3810c700, 0x500, "usb1_dev" }, + [FSL_IMX8MP_USB1] = { 0x38100000, 0xc700, "usb1" }, + [FSL_IMX8MP_GPU2D] = { 0x38008000, 32 * KiB, "gpu2d" }, + [FSL_IMX8MP_GPU3D] = { 0x38000000, 32 * KiB, "gpu3d" }, + [FSL_IMX8MP_QSPI1_RX_BUFFER] = { 0x34000000, 32 * MiB, "qspi1_rx_buffer" }, + [FSL_IMX8MP_PCIE1] = { 0x33800000, 4 * MiB, "pcie1" }, + [FSL_IMX8MP_QSPI1_TX_BUFFER] = { 0x33008000, 32 * KiB, "qspi1_tx_buffer" }, + [FSL_IMX8MP_APBH_DMA] = { 0x33000000, 32 * KiB, "apbh_dma" }, + + /* AIPS-5 Begin */ + [FSL_IMX8MP_MU_3_B] = { 0x30e90000, 64 * KiB, "mu_3_b" }, + [FSL_IMX8MP_MU_3_A] = { 0x30e80000, 64 * KiB, "mu_3_a" }, + [FSL_IMX8MP_MU_2_B] = { 0x30e70000, 64 * KiB, "mu_2_b" }, + [FSL_IMX8MP_MU_2_A] = { 0x30e60000, 64 * KiB, "mu_2_a" }, + [FSL_IMX8MP_EDMA_CHANNELS] = { 0x30e40000, 128 * KiB, "edma_channels" }, + [FSL_IMX8MP_EDMA_MANAGEMENT_PAGE] = { 0x30e30000, 64 * KiB, "edma_management_page" }, + [FSL_IMX8MP_AUDIO_BLK_CTRL] = { 0x30e20000, 64 * KiB, "audio_blk_ctrl" }, + [FSL_IMX8MP_SDMA2] = { 0x30e10000, 64 * KiB, "sdma2" }, + [FSL_IMX8MP_SDMA3] = { 0x30e00000, 64 * KiB, "sdma3" }, + [FSL_IMX8MP_AIPS5_CONFIGURATION] = { 0x30df0000, 64 * KiB, "aips5_configuration" }, + [FSL_IMX8MP_SPBA2] = { 0x30cf0000, 64 * KiB, "spba2" }, + [FSL_IMX8MP_AUDIO_XCVR_RX] = { 0x30cc0000, 64 * KiB, "audio_xcvr_rx" }, + [FSL_IMX8MP_HDMI_TX_AUDLNK_MSTR] = { 0x30cb0000, 64 * KiB, "hdmi_tx_audlnk_mstr" }, + [FSL_IMX8MP_PDM] = { 0x30ca0000, 64 * KiB, "pdm" }, + [FSL_IMX8MP_ASRC] = { 0x30c90000, 64 * KiB, "asrc" }, + [FSL_IMX8MP_SAI7] = { 0x30c80000, 64 * KiB, "sai7" }, + [FSL_IMX8MP_SAI6] = { 0x30c60000, 64 * KiB, "sai6" }, + [FSL_IMX8MP_SAI5] = { 0x30c50000, 64 * KiB, "sai5" }, + [FSL_IMX8MP_SAI3] = { 0x30c30000, 64 * KiB, "sai3" }, + [FSL_IMX8MP_SAI2] = { 0x30c20000, 64 * KiB, "sai2" }, + [FSL_IMX8MP_SAI1] = { 0x30c10000, 64 * KiB, "sai1" }, + /* AIPS-5 End */ + + /* AIPS-4 Begin */ + [FSL_IMX8MP_HDMI_TX] = { 0x32fc0000, 128 * KiB, "hdmi_tx" }, + [FSL_IMX8MP_TZASC] = { 0x32f80000, 64 * KiB, "tzasc" }, + [FSL_IMX8MP_HSIO_BLK_CTL] = { 0x32f10000, 64 * KiB, "hsio_blk_ctl" }, + [FSL_IMX8MP_PCIE_PHY1] = { 0x32f00000, 64 * KiB, "pcie_phy1" }, + [FSL_IMX8MP_MEDIA_BLK_CTL] = { 0x32ec0000, 64 * KiB, "media_blk_ctl" }, + [FSL_IMX8MP_LCDIF2] = { 0x32e90000, 64 * KiB, "lcdif2" }, + [FSL_IMX8MP_LCDIF1] = { 0x32e80000, 64 * KiB, "lcdif1" }, + [FSL_IMX8MP_MIPI_DSI1] = { 0x32e60000, 64 * KiB, "mipi_dsi1" }, + [FSL_IMX8MP_MIPI_CSI2] = { 0x32e50000, 64 * KiB, "mipi_csi2" }, + [FSL_IMX8MP_MIPI_CSI1] = { 0x32e40000, 64 * KiB, "mipi_csi1" }, + [FSL_IMX8MP_IPS_DEWARP] = { 0x32e30000, 64 * KiB, "ips_dewarp" }, + [FSL_IMX8MP_ISP2] = { 0x32e20000, 64 * KiB, "isp2" }, + [FSL_IMX8MP_ISP1] = { 0x32e10000, 64 * KiB, "isp1" }, + [FSL_IMX8MP_ISI] = { 0x32e00000, 64 * KiB, "isi" }, + [FSL_IMX8MP_AIPS4_CONFIGURATION] = { 0x32df0000, 64 * KiB, "aips4_configuration" }, + /* AIPS-4 End */ + + [FSL_IMX8MP_INTERCONNECT] = { 0x32700000, 1 * MiB, "interconnect" }, + + /* AIPS-3 Begin */ + [FSL_IMX8MP_ENET2_TSN] = { 0x30bf0000, 64 * KiB, "enet2_tsn" }, + [FSL_IMX8MP_ENET1] = { 0x30be0000, 64 * KiB, "enet1" }, + [FSL_IMX8MP_SDMA1] = { 0x30bd0000, 64 * KiB, "sdma1" }, + [FSL_IMX8MP_QSPI] = { 0x30bb0000, 64 * KiB, "qspi" }, + [FSL_IMX8MP_USDHC3] = { 0x30b60000, 64 * KiB, "usdhc3" }, + [FSL_IMX8MP_USDHC2] = { 0x30b50000, 64 * KiB, "usdhc2" }, + [FSL_IMX8MP_USDHC1] = { 0x30b40000, 64 * KiB, "usdhc1" }, + [FSL_IMX8MP_I2C6] = { 0x30ae0000, 64 * KiB, "i2c6" }, + [FSL_IMX8MP_I2C5] = { 0x30ad0000, 64 * KiB, "i2c5" }, + [FSL_IMX8MP_SEMAPHORE_HS] = { 0x30ac0000, 64 * KiB, "semaphore_hs" }, + [FSL_IMX8MP_MU_1_B] = { 0x30ab0000, 64 * KiB, "mu_1_b" }, + [FSL_IMX8MP_MU_1_A] = { 0x30aa0000, 64 * KiB, "mu_1_a" }, + [FSL_IMX8MP_AUD_IRQ_STEER] = { 0x30a80000, 64 * KiB, "aud_irq_steer" }, + [FSL_IMX8MP_UART4] = { 0x30a60000, 64 * KiB, "uart4" }, + [FSL_IMX8MP_I2C4] = { 0x30a50000, 64 * KiB, "i2c4" }, + [FSL_IMX8MP_I2C3] = { 0x30a40000, 64 * KiB, "i2c3" }, + [FSL_IMX8MP_I2C2] = { 0x30a30000, 64 * KiB, "i2c2" }, + [FSL_IMX8MP_I2C1] = { 0x30a20000, 64 * KiB, "i2c1" }, + [FSL_IMX8MP_AIPS3_CONFIGURATION] = { 0x309f0000, 64 * KiB, "aips3_configuration" }, + [FSL_IMX8MP_CAAM] = { 0x30900000, 256 * KiB, "caam" }, + [FSL_IMX8MP_SPBA1] = { 0x308f0000, 64 * KiB, "spba1" }, + [FSL_IMX8MP_FLEXCAN2] = { 0x308d0000, 64 * KiB, "flexcan2" }, + [FSL_IMX8MP_FLEXCAN1] = { 0x308c0000, 64 * KiB, "flexcan1" }, + [FSL_IMX8MP_UART2] = { 0x30890000, 64 * KiB, "uart2" }, + [FSL_IMX8MP_UART3] = { 0x30880000, 64 * KiB, "uart3" }, + [FSL_IMX8MP_UART1] = { 0x30860000, 64 * KiB, "uart1" }, + [FSL_IMX8MP_ECSPI3] = { 0x30840000, 64 * KiB, "ecspi3" }, + [FSL_IMX8MP_ECSPI2] = { 0x30830000, 64 * KiB, "ecspi2" }, + [FSL_IMX8MP_ECSPI1] = { 0x30820000, 64 * KiB, "ecspi1" }, + /* AIPS-3 End */ + + /* AIPS-2 Begin */ + [FSL_IMX8MP_QOSC] = { 0x307f0000, 64 * KiB, "qosc" }, + [FSL_IMX8MP_PERFMON2] = { 0x307d0000, 64 * KiB, "perfmon2" }, + [FSL_IMX8MP_PERFMON1] = { 0x307c0000, 64 * KiB, "perfmon1" }, + [FSL_IMX8MP_GPT4] = { 0x30700000, 64 * KiB, "gpt4" }, + [FSL_IMX8MP_GPT5] = { 0x306f0000, 64 * KiB, "gpt5" }, + [FSL_IMX8MP_GPT6] = { 0x306e0000, 64 * KiB, "gpt6" }, + [FSL_IMX8MP_SYSCNT_CTRL] = { 0x306c0000, 64 * KiB, "syscnt_ctrl" }, + [FSL_IMX8MP_SYSCNT_CMP] = { 0x306b0000, 64 * KiB, "syscnt_cmp" }, + [FSL_IMX8MP_SYSCNT_RD] = { 0x306a0000, 64 * KiB, "syscnt_rd" }, + [FSL_IMX8MP_PWM4] = { 0x30690000, 64 * KiB, "pwm4" }, + [FSL_IMX8MP_PWM3] = { 0x30680000, 64 * KiB, "pwm3" }, + [FSL_IMX8MP_PWM2] = { 0x30670000, 64 * KiB, "pwm2" }, + [FSL_IMX8MP_PWM1] = { 0x30660000, 64 * KiB, "pwm1" }, + [FSL_IMX8MP_AIPS2_CONFIGURATION] = { 0x305f0000, 64 * KiB, "aips2_configuration" }, + /* AIPS-2 End */ + + /* AIPS-1 Begin */ + [FSL_IMX8MP_CSU] = { 0x303e0000, 64 * KiB, "csu" }, + [FSL_IMX8MP_RDC] = { 0x303d0000, 64 * KiB, "rdc" }, + [FSL_IMX8MP_SEMAPHORE2] = { 0x303c0000, 64 * KiB, "semaphore2" }, + [FSL_IMX8MP_SEMAPHORE1] = { 0x303b0000, 64 * KiB, "semaphore1" }, + [FSL_IMX8MP_GPC] = { 0x303a0000, 64 * KiB, "gpc" }, + [FSL_IMX8MP_SRC] = { 0x30390000, 64 * KiB, "src" }, + [FSL_IMX8MP_CCM] = { 0x30380000, 64 * KiB, "ccm" }, + [FSL_IMX8MP_SNVS_HP] = { 0x30370000, 64 * KiB, "snvs_hp" }, + [FSL_IMX8MP_ANA_PLL] = { 0x30360000, 64 * KiB, "ana_pll" }, + [FSL_IMX8MP_OCOTP_CTRL] = { 0x30350000, 64 * KiB, "ocotp_ctrl" }, + [FSL_IMX8MP_IOMUXC_GPR] = { 0x30340000, 64 * KiB, "iomuxc_gpr" }, + [FSL_IMX8MP_IOMUXC] = { 0x30330000, 64 * KiB, "iomuxc" }, + [FSL_IMX8MP_GPT3] = { 0x302f0000, 64 * KiB, "gpt3" }, + [FSL_IMX8MP_GPT2] = { 0x302e0000, 64 * KiB, "gpt2" }, + [FSL_IMX8MP_GPT1] = { 0x302d0000, 64 * KiB, "gpt1" }, + [FSL_IMX8MP_WDOG3] = { 0x302a0000, 64 * KiB, "wdog3" }, + [FSL_IMX8MP_WDOG2] = { 0x30290000, 64 * KiB, "wdog2" }, + [FSL_IMX8MP_WDOG1] = { 0x30280000, 64 * KiB, "wdog1" }, + [FSL_IMX8MP_ANA_OSC] = { 0x30270000, 64 * KiB, "ana_osc" }, + [FSL_IMX8MP_ANA_TSENSOR] = { 0x30260000, 64 * KiB, "ana_tsensor" }, + [FSL_IMX8MP_GPIO5] = { 0x30240000, 64 * KiB, "gpio5" }, + [FSL_IMX8MP_GPIO4] = { 0x30230000, 64 * KiB, "gpio4" }, + [FSL_IMX8MP_GPIO3] = { 0x30220000, 64 * KiB, "gpio3" }, + [FSL_IMX8MP_GPIO2] = { 0x30210000, 64 * KiB, "gpio2" }, + [FSL_IMX8MP_GPIO1] = { 0x30200000, 64 * KiB, "gpio1" }, + [FSL_IMX8MP_AIPS1_CONFIGURATION] = { 0x301f0000, 64 * KiB, "aips1_configuration" }, + /* AIPS-1 End */ + + [FSL_IMX8MP_A53_DAP] = { 0x28000000, 16 * MiB, "a53_dap" }, + [FSL_IMX8MP_PCIE1_MEM] = { 0x18000000, 128 * MiB, "pcie1_mem" }, + [FSL_IMX8MP_QSPI_MEM] = { 0x08000000, 256 * MiB, "qspi_mem" }, + [FSL_IMX8MP_OCRAM] = { 0x00900000, 576 * KiB, "ocram" }, + [FSL_IMX8MP_TCM_DTCM] = { 0x00800000, 128 * KiB, "tcm_dtcm" }, + [FSL_IMX8MP_TCM_ITCM] = { 0x007e0000, 128 * KiB, "tcm_itcm" }, + [FSL_IMX8MP_OCRAM_S] = { 0x00180000, 36 * KiB, "ocram_s" }, + [FSL_IMX8MP_CAAM_MEM] = { 0x00100000, 32 * KiB, "caam_mem" }, + [FSL_IMX8MP_BOOT_ROM_PROTECTED] = { 0x0003f000, 4 * KiB, "boot_rom_protected" }, + [FSL_IMX8MP_BOOT_ROM] = { 0x00000000, 252 * KiB, "boot_rom" }, +}; + +static void fsl_imx8mp_init(Object *obj) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mpState *s = FSL_IMX8MP(obj); + int i; + + for (i = 0; i < MIN(ms->smp.cpus, FSL_IMX8MP_NUM_CPUS); i++) { + g_autofree char *name = g_strdup_printf("cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], + ARM_CPU_TYPE_NAME("cortex-a53")); + } + + object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GICV3); + + object_initialize_child(obj, "ccm", &s->ccm, TYPE_IMX8MP_CCM); + + object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MP_ANALOG); + + object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS); + + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i + 1); + object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL); + } + + for (i = 0; i < FSL_IMX8MP_NUM_GPTS; i++) { + g_autofree char *name = g_strdup_printf("gpt%d", i + 1); + object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX8MP_GPT); + } + object_initialize_child(obj, "gpt5-gpt6-irq", &s->gpt5_gpt6_irq, + TYPE_OR_IRQ); + + for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { + g_autofree char *name = g_strdup_printf("i2c%d", i + 1); + object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C); + } + + for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { + g_autofree char *name = g_strdup_printf("gpio%d", i + 1); + object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO); + } + + for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + g_autofree char *name = g_strdup_printf("usdhc%d", i + 1); + object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC); + } + + for (i = 0; i < FSL_IMX8MP_NUM_USBS; i++) { + g_autofree char *name = g_strdup_printf("usb%d", i); + object_initialize_child(obj, name, &s->usb[i], TYPE_USB_DWC3); + } + + for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { + g_autofree char *name = g_strdup_printf("spi%d", i + 1); + object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI); + } + + for (i = 0; i < FSL_IMX8MP_NUM_WDTS; i++) { + g_autofree char *name = g_strdup_printf("wdt%d", i); + object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT); + } + + object_initialize_child(obj, "eth0", &s->enet, TYPE_IMX_ENET); + + object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST); + object_initialize_child(obj, "pcie_phy", &s->pcie_phy, + TYPE_FSL_IMX8M_PCIE_PHY); +} + +static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + FslImx8mpState *s = FSL_IMX8MP(dev); + DeviceState *gicdev = DEVICE(&s->gic); + int i; + + if (ms->smp.cpus > FSL_IMX8MP_NUM_CPUS) { + error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX8MP, FSL_IMX8MP_NUM_CPUS, ms->smp.cpus); + return; + } + + /* CPUs */ + for (i = 0; i < ms->smp.cpus; i++) { + /* On uniprocessor, the CBAR is set to 0 */ + if (ms->smp.cpus > 1) { + object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar", + fsl_imx8mp_memmap[FSL_IMX8MP_GIC_DIST].addr, + &error_abort); + } + + /* + * CNTFID0 base frequency in Hz of system counter + */ + object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, + &error_abort); + + if (i) { + /* + * Secondary CPUs start in powered-down state (and can be + * powered up via the SRC system reset controller) + */ + object_property_set_bool(OBJECT(&s->cpu[i]), "start-powered-off", + true, &error_abort); + } + + if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { + return; + } + } + + /* GIC */ + { + SysBusDevice *gicsbd = SYS_BUS_DEVICE(&s->gic); + QList *redist_region_count; + + qdev_prop_set_uint32(gicdev, "num-cpu", ms->smp.cpus); + qdev_prop_set_uint32(gicdev, "num-irq", + FSL_IMX8MP_NUM_IRQS + GIC_INTERNAL); + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, ms->smp.cpus); + qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count); + object_property_set_link(OBJECT(&s->gic), "sysmem", + OBJECT(get_system_memory()), &error_fatal); + if (!sysbus_realize(gicsbd, errp)) { + return; + } + sysbus_mmio_map(gicsbd, 0, fsl_imx8mp_memmap[FSL_IMX8MP_GIC_DIST].addr); + sysbus_mmio_map(gicsbd, 1, fsl_imx8mp_memmap[FSL_IMX8MP_GIC_REDIST].addr); + + /* + * Wire the outputs from each CPU's generic timer and the GICv3 + * maintenance interrupt signal to the appropriate GIC PPI inputs, and + * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs. + */ + for (i = 0; i < ms->smp.cpus; i++) { + DeviceState *cpudev = DEVICE(&s->cpu[i]); + int intidbase = FSL_IMX8MP_NUM_IRQS + i * GIC_INTERNAL; + qemu_irq irq; + + /* + * Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + static const int timer_irqs[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { + irq = qdev_get_gpio_in(gicdev, intidbase + timer_irqs[j]); + qdev_connect_gpio_out(cpudev, j, irq); + } + + irq = qdev_get_gpio_in(gicdev, intidbase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + + irq = qdev_get_gpio_in(gicdev, intidbase + VIRTUAL_PMU_IRQ); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, irq); + + sysbus_connect_irq(gicsbd, i, + qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicsbd, i + ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + } + } + + /* CCM */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_CCM].addr); + + /* Analog */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->analog), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_ANA_PLL].addr); + + /* UARTs */ + for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } serial_table[FSL_IMX8MP_NUM_UARTS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_UART1].addr, FSL_IMX8MP_UART1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART2].addr, FSL_IMX8MP_UART2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART3].addr, FSL_IMX8MP_UART3_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_UART4].addr, FSL_IMX8MP_UART4_IRQ }, + }; + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(gicdev, serial_table[i].irq)); + } + + /* GPTs */ + object_property_set_int(OBJECT(&s->gpt5_gpt6_irq), "num-lines", 2, + &error_abort); + if (!qdev_realize(DEVICE(&s->gpt5_gpt6_irq), NULL, errp)) { + return; + } + + qdev_connect_gpio_out(DEVICE(&s->gpt5_gpt6_irq), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_GPT5_GPT6_IRQ)); + + for (i = 0; i < FSL_IMX8MP_NUM_GPTS; i++) { + hwaddr gpt_addrs[FSL_IMX8MP_NUM_GPTS] = { + fsl_imx8mp_memmap[FSL_IMX8MP_GPT1].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT2].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT3].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT4].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT5].addr, + fsl_imx8mp_memmap[FSL_IMX8MP_GPT6].addr, + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_addrs[i]); + + if (i < FSL_IMX8MP_NUM_GPTS - 2) { + static const unsigned int gpt_irqs[FSL_IMX8MP_NUM_GPTS - 2] = { + FSL_IMX8MP_GPT1_IRQ, + FSL_IMX8MP_GPT2_IRQ, + FSL_IMX8MP_GPT3_IRQ, + FSL_IMX8MP_GPT4_IRQ, + }; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(gicdev, gpt_irqs[i])); + } else { + int irq = i - FSL_IMX8MP_NUM_GPTS + 2; + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->gpt5_gpt6_irq), irq)); + } + } + + /* I2Cs */ + for (i = 0; i < FSL_IMX8MP_NUM_I2CS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } i2c_table[FSL_IMX8MP_NUM_I2CS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C1].addr, FSL_IMX8MP_I2C1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C2].addr, FSL_IMX8MP_I2C2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C3].addr, FSL_IMX8MP_I2C3_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C4].addr, FSL_IMX8MP_I2C4_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C5].addr, FSL_IMX8MP_I2C5_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_I2C6].addr, FSL_IMX8MP_I2C6_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(gicdev, i2c_table[i].irq)); + } + + /* GPIOs */ + for (i = 0; i < FSL_IMX8MP_NUM_GPIOS; i++) { + struct { + hwaddr addr; + unsigned int irq_low; + unsigned int irq_high; + } gpio_table[FSL_IMX8MP_NUM_GPIOS] = { + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO1].addr, + FSL_IMX8MP_GPIO1_LOW_IRQ, + FSL_IMX8MP_GPIO1_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO2].addr, + FSL_IMX8MP_GPIO2_LOW_IRQ, + FSL_IMX8MP_GPIO2_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO3].addr, + FSL_IMX8MP_GPIO3_LOW_IRQ, + FSL_IMX8MP_GPIO3_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO4].addr, + FSL_IMX8MP_GPIO4_LOW_IRQ, + FSL_IMX8MP_GPIO4_HIGH_IRQ + }, + { + fsl_imx8mp_memmap[FSL_IMX8MP_GPIO5].addr, + FSL_IMX8MP_GPIO5_LOW_IRQ, + FSL_IMX8MP_GPIO5_HIGH_IRQ + }, + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), "has-edge-sel", true, + &error_abort); + object_property_set_bool(OBJECT(&s->gpio[i]), "has-upper-pin-irq", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_low)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(gicdev, gpio_table[i].irq_high)); + } + + /* USDHCs */ + for (i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } usdhc_table[FSL_IMX8MP_NUM_USDHCS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC1].addr, FSL_IMX8MP_USDHC1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC2].addr, FSL_IMX8MP_USDHC2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC3].addr, FSL_IMX8MP_USDHC3_IRQ }, + }; + + object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", + SDHCI_VENDOR_IMX, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, usdhc_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + qdev_get_gpio_in(gicdev, usdhc_table[i].irq)); + } + + /* USBs */ + for (i = 0; i < FSL_IMX8MP_NUM_USBS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } usb_table[FSL_IMX8MP_NUM_USBS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_USB1].addr, FSL_IMX8MP_USB1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_USB2].addr, FSL_IMX8MP_USB2_IRQ }, + }; + + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p2", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p3", 1); + qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 0, + qdev_get_gpio_in(gicdev, usb_table[i].irq)); + } + + /* ECSPIs */ + for (i = 0; i < FSL_IMX8MP_NUM_ECSPIS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } spi_table[FSL_IMX8MP_NUM_ECSPIS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI1].addr, FSL_IMX8MP_ECSPI1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI2].addr, FSL_IMX8MP_ECSPI2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_ECSPI3].addr, FSL_IMX8MP_ECSPI3_IRQ }, + }; + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(gicdev, spi_table[i].irq)); + } + + /* ENET1 */ + object_property_set_uint(OBJECT(&s->enet), "phy-num", s->phy_num, + &error_abort); + object_property_set_uint(OBJECT(&s->enet), "tx-ring-num", 3, &error_abort); + qemu_configure_nic_device(DEVICE(&s->enet), true, NULL); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->enet), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->enet), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_ENET1].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_ENET1_MAC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 1, + qdev_get_gpio_in(gicdev, FSL_IMX6_ENET1_MAC_1588_IRQ)); + + /* SNVS */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_SNVS_HP].addr); + + /* Watchdogs */ + for (i = 0; i < FSL_IMX8MP_NUM_WDTS; i++) { + struct { + hwaddr addr; + unsigned int irq; + } wdog_table[FSL_IMX8MP_NUM_WDTS] = { + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG1].addr, FSL_IMX8MP_WDOG1_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG2].addr, FSL_IMX8MP_WDOG2_IRQ }, + { fsl_imx8mp_memmap[FSL_IMX8MP_WDOG3].addr, FSL_IMX8MP_WDOG3_IRQ }, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), "pretimeout-support", + true, &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) { + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, wdog_table[i].addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0, + qdev_get_gpio_in(gicdev, wdog_table[i].irq)); + } + + /* PCIe */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_PCIE1].addr); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTA_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTB_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTC_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_INTD_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4, + qdev_get_gpio_in(gicdev, FSL_IMX8MP_PCI_MSI_IRQ)); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0, + fsl_imx8mp_memmap[FSL_IMX8MP_PCIE_PHY1].addr); + + /* On-Chip RAM */ + if (!memory_region_init_ram(&s->ocram, NULL, "imx8mp.ocram", + fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].size, + errp)) { + return; + } + memory_region_add_subregion(get_system_memory(), + fsl_imx8mp_memmap[FSL_IMX8MP_OCRAM].addr, + &s->ocram); + + /* Unimplemented devices */ + for (i = 0; i < ARRAY_SIZE(fsl_imx8mp_memmap); i++) { + switch (i) { + case FSL_IMX8MP_ANA_PLL: + case FSL_IMX8MP_CCM: + case FSL_IMX8MP_GIC_DIST: + case FSL_IMX8MP_GIC_REDIST: + case FSL_IMX8MP_GPIO1 ... FSL_IMX8MP_GPIO5: + case FSL_IMX8MP_ECSPI1 ... FSL_IMX8MP_ECSPI3: + case FSL_IMX8MP_ENET1: + case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6: + case FSL_IMX8MP_OCRAM: + case FSL_IMX8MP_PCIE1: + case FSL_IMX8MP_PCIE_PHY1: + case FSL_IMX8MP_RAM: + case FSL_IMX8MP_SNVS_HP: + case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4: + case FSL_IMX8MP_USB1 ... FSL_IMX8MP_USB2: + case FSL_IMX8MP_USDHC1 ... FSL_IMX8MP_USDHC3: + case FSL_IMX8MP_WDOG1 ... FSL_IMX8MP_WDOG3: + /* device implemented and treated above */ + break; + + default: + create_unimplemented_device(fsl_imx8mp_memmap[i].name, + fsl_imx8mp_memmap[i].addr, + fsl_imx8mp_memmap[i].size); + break; + } + } +} + +static const Property fsl_imx8mp_properties[] = { + DEFINE_PROP_UINT32("fec1-phy-num", FslImx8mpState, phy_num, 0), + DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mpState, phy_connected, true), +}; + +static void fsl_imx8mp_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, fsl_imx8mp_properties); + dc->realize = fsl_imx8mp_realize; + + dc->desc = "i.MX 8M Plus SoC"; +} + +static const TypeInfo fsl_imx8mp_types[] = { + { + .name = TYPE_FSL_IMX8MP, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslImx8mpState), + .instance_init = fsl_imx8mp_init, + .class_init = fsl_imx8mp_class_init, + }, +}; + +DEFINE_TYPES(fsl_imx8mp_types) diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c new file mode 100644 index 0000000000..e1a7892fd7 --- /dev/null +++ b/hw/arm/imx8mp-evk.c @@ -0,0 +1,74 @@ +/* + * NXP i.MX 8M Plus Evaluation Kit System Emulation + * + * Copyright (c) 2024, Bernhard Beschow <shentey@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "exec/address-spaces.h" +#include "hw/arm/boot.h" +#include "hw/arm/fsl-imx8mp.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "system/qtest.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + +static void imx8mp_evk_init(MachineState *machine) +{ + static struct arm_boot_info boot_info; + FslImx8mpState *s; + + if (machine->ram_size > FSL_IMX8MP_RAM_SIZE_MAX) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08" PRIx64 ")", + machine->ram_size, FSL_IMX8MP_RAM_SIZE_MAX); + exit(1); + } + + boot_info = (struct arm_boot_info) { + .loader_start = FSL_IMX8MP_RAM_START, + .board_id = -1, + .ram_size = machine->ram_size, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, + }; + + s = FSL_IMX8MP(object_new(TYPE_FSL_IMX8MP)); + object_property_add_child(OBJECT(machine), "soc", OBJECT(s)); + object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal); + qdev_realize(DEVICE(s), NULL, &error_fatal); + + memory_region_add_subregion(get_system_memory(), FSL_IMX8MP_RAM_START, + machine->ram); + + for (int i = 0; i < FSL_IMX8MP_NUM_USDHCS; i++) { + BusState *bus; + DeviceState *carddev; + BlockBackend *blk; + DriveInfo *di = drive_get(IF_SD, i, 0); + + if (!di) { + continue; + } + + blk = blk_by_legacy_dinfo(di); + bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus"); + carddev = qdev_new(TYPE_SD_CARD); + qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal); + qdev_realize_and_unref(carddev, bus, &error_fatal); + } + + if (!qtest_enabled()) { + arm_load_kernel(&s->cpu[0], machine, &boot_info); + } +} + +static void imx8mp_evk_machine_init(MachineClass *mc) +{ + mc->desc = "NXP i.MX 8M Plus EVK Board"; + mc->init = imx8mp_evk_init; + mc->max_cpus = FSL_IMX8MP_NUM_CPUS; + mc->default_ram_id = "imx8mp-evk.ram"; +} +DEFINE_MACHINE("imx8mp-evk", imx8mp_evk_machine_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 465c757f97..ac473ce7cd 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -54,6 +54,8 @@ arm_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) arm_ss.add(when: 'CONFIG_MUSCA', if_true: files('musca.c')) arm_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) +arm_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c')) +arm_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c')) arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index b49a59b64c..b40acbe024 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -377,7 +377,7 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, SMMUTransCfg *cfg, qemu_log_mask(LOG_GUEST_ERROR, "Cannot fetch pte at address=0x%"PRIx64"\n", addr); event->type = SMMU_EVT_F_CD_FETCH; - event->u.f_ste_fetch.addr = addr; + event->u.f_cd_fetch.addr = addr; return -EINVAL; } for (i = 0; i < ARRAY_SIZE(buf->word); i++) { diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 4a5a9666e9..ee69081ef4 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -53,6 +53,7 @@ #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" +#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/pci-host/gpex.h" @@ -192,6 +193,10 @@ static const MemMapEntry base_memmap[] = { [VIRT_MEM] = { GiB, LEGACY_RAMLIMIT_BYTES }, }; +/* Update the docs for highmem-mmio-size when changing this default */ +#define DEFAULT_HIGH_PCIE_MMIO_SIZE_GB 512 +#define DEFAULT_HIGH_PCIE_MMIO_SIZE (DEFAULT_HIGH_PCIE_MMIO_SIZE_GB * GiB) + /* * Highmem IO Regions: This memory map is floating, located after the RAM. * Each MemMapEntry base (GPA) will be dynamically computed, depending on the @@ -207,13 +212,16 @@ static const MemMapEntry base_memmap[] = { * PA space for one specific region is always reserved, even if the region * has been disabled or doesn't fit into the PA space. However, the PA space * for the region won't be reserved in these circumstances with compact layout. + * + * Note that the highmem-mmio-size property will update the high PCIE MMIO size + * field in this array. */ static MemMapEntry extended_memmap[] = { /* Additional 64 MB redist region (can contain up to 512 redistributors) */ [VIRT_HIGH_GIC_REDIST2] = { 0x0, 64 * MiB }, [VIRT_HIGH_PCIE_ECAM] = { 0x0, 256 * MiB }, /* Second PCIe window */ - [VIRT_HIGH_PCIE_MMIO] = { 0x0, 512 * GiB }, + [VIRT_HIGH_PCIE_MMIO] = { 0x0, DEFAULT_HIGH_PCIE_MMIO_SIZE }, }; static const int a15irqmap[] = { @@ -2550,6 +2558,40 @@ static void virt_set_highmem_mmio(Object *obj, bool value, Error **errp) vms->highmem_mmio = value; } +static void virt_get_highmem_mmio_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint64_t size = extended_memmap[VIRT_HIGH_PCIE_MMIO].size; + + visit_type_size(v, name, &size, errp); +} + +static void virt_set_highmem_mmio_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint64_t size; + + if (!visit_type_size(v, name, &size, errp)) { + return; + } + + if (!is_power_of_2(size)) { + error_setg(errp, "highmem-mmio-size is not a power of 2"); + return; + } + + if (size < DEFAULT_HIGH_PCIE_MMIO_SIZE) { + char *sz = size_to_str(DEFAULT_HIGH_PCIE_MMIO_SIZE); + error_setg(errp, "highmem-mmio-size cannot be set to a lower value " + "than the default (%s)", sz); + g_free(sz); + return; + } + + extended_memmap[VIRT_HIGH_PCIE_MMIO].size = size; +} static bool virt_get_its(Object *obj, Error **errp) { @@ -3207,6 +3249,14 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) "Set on/off to enable/disable high " "memory region for PCI MMIO"); + object_class_property_add(oc, "highmem-mmio-size", "size", + virt_get_highmem_mmio_size, + virt_set_highmem_mmio_size, + NULL, NULL); + object_class_property_set_description(oc, "highmem-mmio-size", + "Set the high memory region size " + "for PCI MMIO"); + object_class_property_add_str(oc, "gic-version", virt_get_gic_version, virt_set_gic_version); object_class_property_set_description(oc, "gic-version", diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 9bdd4fa17c..6eb66406b0 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -32,8 +32,8 @@ #define FIT_LOADER_MAX_PATH (128) -static const void *fit_load_image_alloc(const void *itb, const char *name, - int *poff, size_t *psz, Error **errp) +static void *fit_load_image_alloc(const void *itb, const char *name, + int *poff, size_t *psz, Error **errp) { const void *data; const char *comp; @@ -80,11 +80,11 @@ static const void *fit_load_image_alloc(const void *itb, const char *name, return NULL; } - data = g_realloc(uncomp_data, uncomp_len); + uncomp_data = g_realloc(uncomp_data, uncomp_len); if (psz) { *psz = uncomp_len; } - return data; + return uncomp_data; } error_setg(errp, "unknown compression '%s'", comp); @@ -177,13 +177,12 @@ out: static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, int cfg, void *opaque, const void *match_data, - hwaddr kernel_end, Error **errp) + hwaddr kernel_end, void **pfdt, Error **errp) { ERRP_GUARD(); Error *err = NULL; const char *name; - const void *data; - const void *load_data; + void *data; hwaddr load_addr; int img_off; size_t sz; @@ -194,7 +193,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, return 0; } - load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); + data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); if (!data) { error_prepend(errp, "unable to load FDT image from FIT: "); return -EINVAL; @@ -211,19 +210,23 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, } if (ldr->fdt_filter) { - load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + void *filtered_data; + + filtered_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + if (filtered_data != data) { + g_free(data); + data = filtered_data; + } } load_addr = ldr->addr_to_phys(opaque, load_addr); - sz = fdt_totalsize(load_data); - rom_add_blob_fixed(name, load_data, sz, load_addr); + sz = fdt_totalsize(data); + rom_add_blob_fixed(name, data, sz, load_addr); - ret = 0; + *pfdt = data; + return 0; out: g_free((void *) data); - if (data != load_data) { - g_free((void *) load_data); - } return ret; } @@ -259,7 +262,8 @@ out: return ret; } -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) +int load_fit(const struct fit_loader *ldr, const char *filename, + void **pfdt, void *opaque) { Error *err = NULL; const struct fit_loader_match *match; @@ -323,7 +327,7 @@ int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) goto out; } - ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, + ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, pfdt, &err); if (ret) { error_report_err(err); diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 4e020c358b..0be0ac044c 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -332,6 +332,8 @@ bool machine_parse_smp_cache(MachineState *ms, return false; } } + + mc->smp_props.has_caches = true; return true; } diff --git a/hw/core/machine.c b/hw/core/machine.c index 02cff735b3..b68b8b94a3 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -19,6 +19,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-visit-machine.h" +#include "qapi/qapi-commands-machine.h" #include "qemu/madvise.h" #include "qom/object_interfaces.h" #include "system/cpus.h" @@ -1696,6 +1697,22 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify) notifier_remove(notify); } +static void handle_machine_dumpdtb(MachineState *ms) +{ + if (!ms->dumpdtb) { + return; + } +#ifdef CONFIG_FDT + qmp_dumpdtb(ms->dumpdtb, &error_fatal); + exit(0); +#else + error_report("This machine doesn't have an FDT"); + error_printf("(this machine type definitely doesn't use FDT, and " + "this QEMU doesn't have FDT support compiled in)\n"); + exit(1); +#endif +} + void qdev_machine_creation_done(void) { cpu_synchronize_all_post_init(); @@ -1712,6 +1729,12 @@ void qdev_machine_creation_done(void) phase_advance(PHASE_MACHINE_READY); qdev_assert_realized_properly(); + /* + * If the user used -machine dumpdtb=file.dtb to request that we + * dump the DTB to a file, do it now, and exit. + */ + handle_machine_dumpdtb(current_machine); + /* TODO: once all bus devices are qdevified, this should be done * when bus is created by qdev.c */ /* diff --git a/hw/display/Kconfig b/hw/display/Kconfig index 2b53dfd7d2..1e95ab28ef 100644 --- a/hw/display/Kconfig +++ b/hw/display/Kconfig @@ -141,10 +141,6 @@ config XLNX_DISPLAYPORT config DM163 bool -config MAC_PVG - bool - default y - config MAC_PVG_MMIO bool depends on MAC_PVG && AARCH64 diff --git a/hw/display/meson.build b/hw/display/meson.build index 94f4f05d36..90e6c041bd 100644 --- a/hw/display/meson.build +++ b/hw/display/meson.build @@ -61,13 +61,8 @@ system_ss.add(when: 'CONFIG_ARTIST', if_true: files('artist.c')) system_ss.add(when: 'CONFIG_ATI_VGA', if_true: [files('ati.c', 'ati_2d.c', 'ati_dbg.c'), pixman]) -if host_os == 'darwin' - system_ss.add(when: 'CONFIG_MAC_PVG', if_true: [files('apple-gfx.m'), pvg, metal]) - system_ss.add(when: 'CONFIG_MAC_PVG_PCI', if_true: [files('apple-gfx-pci.m'), pvg, metal]) - if cpu == 'aarch64' - system_ss.add(when: 'CONFIG_MAC_PVG_MMIO', if_true: [files('apple-gfx-mmio.m'), pvg, metal]) - endif -endif +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_PCI'], if_true: [files('apple-gfx.m', 'apple-gfx-pci.m')]) +system_ss.add(when: [pvg, 'CONFIG_MAC_PVG_MMIO'], if_true: [files('apple-gfx.m', 'apple-gfx-mmio.m')]) if config_all_devices.has_key('CONFIG_VIRTIO_GPU') virtio_gpu_ss = ss.source_set() diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig index c423e10f59..a209294c20 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -16,6 +16,14 @@ config SIFIVE_GPIO config STM32L4X5_GPIO bool +config PCA9552 + bool + depends on I2C + +config PCA9554 + bool + depends on I2C + config PCF8574 bool depends on I2C diff --git a/hw/i386/pc.c b/hw/i386/pc.c index f199a8c7ad..63a96cd23f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1798,6 +1798,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) mc->nvdimm_supported = true; mc->smp_props.dies_supported = true; mc->smp_props.modules_supported = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1D] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L1I] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L2] = true; + mc->smp_props.cache_supported[CACHE_LEVEL_AND_TYPE_L3] = true; mc->default_ram_id = "pc.ram"; pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO; diff --git a/hw/loongarch/virt-fdt-build.c b/hw/loongarch/virt-fdt-build.c index dbc269afba..728ce46699 100644 --- a/hw/loongarch/virt-fdt-build.c +++ b/hw/loongarch/virt-fdt-build.c @@ -527,7 +527,6 @@ void virt_fdt_setup(LoongArchVirtMachineState *lvms) * Put the FDT into the memory map as a ROM image: this will ensure * the FDT is copied again upon reset, even if addr points into RAM. */ - qemu_fdt_dumpdtb(machine->fdt, lvms->fdt_size); rom_add_blob_fixed_as("fdt", machine->fdt, lvms->fdt_size, FDT_BASE, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index f2aa0a9782..59533b058b 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -45,14 +45,6 @@ #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" -static bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) -{ - if (lvms->veiointc == ON_OFF_AUTO_OFF) { - return false; - } - return true; -} - static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 4690b254dd..149a263bd5 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -358,8 +358,8 @@ static void gen_firmware(void *p, hwaddr kernel_entry, hwaddr fdt_addr) kernel_entry); } -static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, - const void *match_data, hwaddr *load_addr) +static void *boston_fdt_filter(void *opaque, const void *fdt_orig, + const void *match_data, hwaddr *load_addr) { BostonState *s = BOSTON(opaque); MachineState *machine = s->mach; @@ -395,7 +395,6 @@ static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, 1, ram_high_sz); fdt = g_realloc(fdt, fdt_totalsize(fdt)); - qemu_fdt_dumpdtb(fdt, fdt_sz); s->fdt_base = *load_addr; @@ -797,7 +796,7 @@ static void boston_mach_init(MachineState *machine) if (kernel_size > 0) { int dt_size; g_autofree const void *dtb_file_data = NULL; - g_autofree const void *dtb_load_data = NULL; + void *dtb_load_data = NULL; hwaddr dtb_paddr = QEMU_ALIGN_UP(kernel_high, 64 * KiB); hwaddr dtb_vaddr = cpu_mips_phys_to_kseg0(NULL, dtb_paddr); @@ -810,6 +809,12 @@ static void boston_mach_init(MachineState *machine) dtb_load_data = boston_fdt_filter(s, dtb_file_data, NULL, &dtb_vaddr); + if (!dtb_load_data) { + /* boston_fdt_filter() already printed the error for us */ + exit(1); + } + + machine->fdt = dtb_load_data; /* Calculate real fdt size after filter */ dt_size = fdt_totalsize(dtb_load_data); @@ -818,7 +823,8 @@ static void boston_mach_init(MachineState *machine) rom_ptr(dtb_paddr, dt_size)); } else { /* Try to load file as FIT */ - fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, s); + fit_err = load_fit(&boston_fit_loader, machine->kernel_filename, + &machine->fdt, s); if (fit_err) { error_report("unable to load kernel image"); exit(1); diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 8f9ce2f68c..82bd68b4bb 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -30,14 +30,6 @@ config EDU default y if TEST_DEVICES depends on PCI && MSI_NONBROKEN -config PCA9552 - bool - depends on I2C - -config PCA9554 - bool - depends on I2C - config I2C_ECHO bool default y if TEST_DEVICES @@ -86,6 +78,12 @@ config IMX select SSI select USB_EHCI_SYSBUS +config FSL_IMX8MP_ANALOG + bool + +config FSL_IMX8MP_CCM + bool + config STM32_RCC bool diff --git a/hw/misc/imx8mp_analog.c b/hw/misc/imx8mp_analog.c new file mode 100644 index 0000000000..f7e7c83cc4 --- /dev/null +++ b/hw/misc/imx8mp_analog.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * i.MX 8M Plus ANALOG IP block emulation code + * + * Based on hw/misc/imx7_ccm.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/misc/imx8mp_analog.h" +#include "migration/vmstate.h" + +#define ANALOG_PLL_LOCK BIT(31) + +static void imx8mp_analog_reset(DeviceState *dev) +{ + IMX8MPAnalogState *s = IMX8MP_ANALOG(dev); + + memset(s->analog, 0, sizeof(s->analog)); + + s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL1_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL1_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL2_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_AUDIO_PLL2_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL0] = 0x00145032; + s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_VIDEO_PLL1_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_VIDEO_PLL1_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_DRAM_PLL_GEN_CTRL] = 0x00002010; + s->analog[ANALOG_DRAM_PLL_FDIV_CTL0] = 0x0012c032; + s->analog[ANALOG_DRAM_PLL_FDIV_CTL1] = 0x00000000; + s->analog[ANALOG_DRAM_PLL_SSCG_CTRL] = 0x00000000; + s->analog[ANALOG_DRAM_PLL_MNIT_CTRL] = 0x00100103; + s->analog[ANALOG_GPU_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_GPU_PLL_FDIV_CTL0] = 0x000c8031; + s->analog[ANALOG_GPU_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_GPU_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_VPU_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_VPU_PLL_FDIV_CTL0] = 0x0012c032; + s->analog[ANALOG_VPU_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_VPU_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_ARM_PLL_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_ARM_PLL_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_ARM_PLL_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL1_GEN_CTRL] = 0x0aaaa810; + s->analog[ANALOG_SYS_PLL1_FDIV_CTL0] = 0x00190032; + s->analog[ANALOG_SYS_PLL1_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL1_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL2_GEN_CTRL] = 0x0aaaa810; + s->analog[ANALOG_SYS_PLL2_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_SYS_PLL2_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL2_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_SYS_PLL3_GEN_CTRL] = 0x00000810; + s->analog[ANALOG_SYS_PLL3_FDIV_CTL0] = 0x000fa031; + s->analog[ANALOG_SYS_PLL3_LOCKD_CTRL] = 0x0010003f; + s->analog[ANALOG_SYS_PLL3_MNIT_CTRL] = 0x00280081; + s->analog[ANALOG_OSC_MISC_CFG] = 0x00000000; + s->analog[ANALOG_ANAMIX_PLL_MNIT_CTL] = 0x00000000; + s->analog[ANALOG_DIGPROG] = 0x00824010; + + /* all PLLs need to be locked */ + s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_DRAM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_GPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_VPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_ARM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK; + s->analog[ANALOG_SYS_PLL3_GEN_CTRL] |= ANALOG_PLL_LOCK; +} + +static uint64_t imx8mp_analog_read(void *opaque, hwaddr offset, unsigned size) +{ + IMX8MPAnalogState *s = opaque; + + return s->analog[offset >> 2]; +} + +static void imx8mp_analog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IMX8MPAnalogState *s = opaque; + + if (offset >> 2 == ANALOG_DIGPROG) { + qemu_log_mask(LOG_GUEST_ERROR, + "Guest write to read-only ANALOG_DIGPROG register\n"); + } else { + s->analog[offset >> 2] = value; + } +} + +static const struct MemoryRegionOps imx8mp_analog_ops = { + .read = imx8mp_analog_read, + .write = imx8mp_analog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx8mp_analog_init(Object *obj) +{ + IMX8MPAnalogState *s = IMX8MP_ANALOG(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + + memory_region_init(&s->mmio.container, obj, TYPE_IMX8MP_ANALOG, 0x10000); + + memory_region_init_io(&s->mmio.analog, obj, &imx8mp_analog_ops, s, + TYPE_IMX8MP_ANALOG, sizeof(s->analog)); + memory_region_add_subregion(&s->mmio.container, 0, &s->mmio.analog); + + sysbus_init_mmio(sd, &s->mmio.container); +} + +static const VMStateDescription imx8mp_analog_vmstate = { + .name = TYPE_IMX8MP_ANALOG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(analog, IMX8MPAnalogState, ANALOG_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx8mp_analog_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_legacy_reset(dc, imx8mp_analog_reset); + dc->vmsd = &imx8mp_analog_vmstate; + dc->desc = "i.MX 8M Plus Analog Module"; +} + +static const TypeInfo imx8mp_analog_types[] = { + { + .name = TYPE_IMX8MP_ANALOG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMX8MPAnalogState), + .instance_init = imx8mp_analog_init, + .class_init = imx8mp_analog_class_init, + } +}; + +DEFINE_TYPES(imx8mp_analog_types); diff --git a/hw/misc/imx8mp_ccm.c b/hw/misc/imx8mp_ccm.c new file mode 100644 index 0000000000..1a1c932427 --- /dev/null +++ b/hw/misc/imx8mp_ccm.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * i.MX 8M Plus CCM IP block emulation code + * + * Based on hw/misc/imx7_ccm.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" + +#include "hw/misc/imx8mp_ccm.h" +#include "migration/vmstate.h" + +#include "trace.h" + +#define CKIH_FREQ 16000000 /* 16MHz crystal input */ + +static void imx8mp_ccm_reset(DeviceState *dev) +{ + IMX8MPCCMState *s = IMX8MP_CCM(dev); + + memset(s->ccm, 0, sizeof(s->ccm)); +} + +#define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t)) +#define CCM_BITOP(offset) ((offset) & (hwaddr)0xF) + +enum { + CCM_BITOP_NONE = 0x00, + CCM_BITOP_SET = 0x04, + CCM_BITOP_CLR = 0x08, + CCM_BITOP_TOG = 0x0C, +}; + +static uint64_t imx8mp_set_clr_tog_read(void *opaque, hwaddr offset, + unsigned size) +{ + const uint32_t *mmio = opaque; + + return mmio[CCM_INDEX(offset)]; +} + +static void imx8mp_set_clr_tog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + const uint8_t bitop = CCM_BITOP(offset); + const uint32_t index = CCM_INDEX(offset); + uint32_t *mmio = opaque; + + switch (bitop) { + case CCM_BITOP_NONE: + mmio[index] = value; + break; + case CCM_BITOP_SET: + mmio[index] |= value; + break; + case CCM_BITOP_CLR: + mmio[index] &= ~value; + break; + case CCM_BITOP_TOG: + mmio[index] ^= value; + break; + }; +} + +static const struct MemoryRegionOps imx8mp_set_clr_tog_ops = { + .read = imx8mp_set_clr_tog_read, + .write = imx8mp_set_clr_tog_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx8mp_ccm_init(Object *obj) +{ + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX8MPCCMState *s = IMX8MP_CCM(obj); + + memory_region_init_io(&s->iomem, + obj, + &imx8mp_set_clr_tog_ops, + s->ccm, + TYPE_IMX8MP_CCM ".ccm", + sizeof(s->ccm)); + + sysbus_init_mmio(sd, &s->iomem); +} + +static const VMStateDescription imx8mp_ccm_vmstate = { + .name = TYPE_IMX8MP_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(ccm, IMX8MPCCMState, CCM_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static uint32_t imx8mp_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + /* + * This function is "consumed" by GPT emulation code. Some clocks + * have fixed frequencies and we can provide requested frequency + * easily. However for CCM provided clocks (like IPG) each GPT + * timer can have its own clock root. + * This means we need additional information when calling this + * function to know the requester's identity. + */ + uint32_t freq = 0; + + switch (clock) { + case CLK_NONE: + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + case CLK_HIGH: + freq = CKIH_FREQ; + break; + case CLK_IPG: + case CLK_IPG_HIGH: + /* + * For now we don't have a way to figure out the device this + * function is called for. Until then the IPG derived clocks + * are left unimplemented. + */ + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n", + TYPE_IMX8MP_CCM, __func__, clock); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX8MP_CCM, __func__, clock); + break; + } + + trace_ccm_clock_freq(clock, freq); + + return freq; +} + +static void imx8mp_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + device_class_set_legacy_reset(dc, imx8mp_ccm_reset); + dc->vmsd = &imx8mp_ccm_vmstate; + dc->desc = "i.MX 8M Plus Clock Control Module"; + + ccm->get_clock_frequency = imx8mp_ccm_get_clock_frequency; +} + +static const TypeInfo imx8mp_ccm_types[] = { + { + .name = TYPE_IMX8MP_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX8MPCCMState), + .instance_init = imx8mp_ccm_init, + .class_init = imx8mp_ccm_class_init, + }, +}; + +DEFINE_TYPES(imx8mp_ccm_types); diff --git a/hw/misc/meson.build b/hw/misc/meson.build index edd36a334d..0b5187a2f7 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -55,6 +55,8 @@ system_ss.add(when: 'CONFIG_AXP2XX_PMU', if_true: files('axp2xx.c')) system_ss.add(when: 'CONFIG_REALVIEW', if_true: files('arm_sysctl.c')) system_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c')) +system_ss.add(when: 'CONFIG_FSL_IMX8MP_ANALOG', if_true: files('imx8mp_analog.c')) +system_ss.add(when: 'CONFIG_FSL_IMX8MP_CCM', if_true: files('imx8mp_ccm.c')) system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx25_ccm.c', 'imx31_ccm.c', diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c index d1f29759d5..0e85974cf9 100644 --- a/hw/misc/npcm_clk.c +++ b/hw/misc/npcm_clk.c @@ -964,8 +964,9 @@ static void npcm_clk_enter_reset(Object *obj, ResetType type) NPCMCLKState *s = NPCM_CLK(obj); NPCMCLKClass *c = NPCM_CLK_GET_CLASS(s); - g_assert(sizeof(s->regs) >= c->nr_regs * sizeof(uint32_t)); - memcpy(s->regs, c->cold_reset_values, sizeof(s->regs)); + size_t sizeof_regs = c->nr_regs * sizeof(uint32_t); + g_assert(sizeof(s->regs) >= sizeof_regs); + memcpy(s->regs, c->cold_reset_values, sizeof_regs); s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); npcm7xx_clk_update_all_clocks(s); /* diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 8175751518..e62c6a3588 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -266,7 +266,7 @@ static const uint32_t nvme_feature_cap[NVME_FID_MAX] = { [NVME_FDP_EVENTS] = NVME_FEAT_CAP_CHANGE | NVME_FEAT_CAP_NS, }; -static const uint32_t nvme_cse_acs[256] = { +static const uint32_t nvme_cse_acs_default[256] = { [NVME_ADM_CMD_DELETE_SQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_CREATE_SQ] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_GET_LOG_PAGE] = NVME_CMD_EFF_CSUPP, @@ -277,17 +277,14 @@ static const uint32_t nvme_cse_acs[256] = { [NVME_ADM_CMD_SET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_GET_FEATURES] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_ASYNC_EV_REQ] = NVME_CMD_EFF_CSUPP, - [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC, - [NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP, - [NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP, + [NVME_ADM_CMD_NS_ATTACHMENT] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_NIC | + NVME_CMD_EFF_CCC, [NVME_ADM_CMD_FORMAT_NVM] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_ADM_CMD_DIRECTIVE_RECV] = NVME_CMD_EFF_CSUPP, [NVME_ADM_CMD_DIRECTIVE_SEND] = NVME_CMD_EFF_CSUPP, }; -static const uint32_t nvme_cse_iocs_none[256]; - -static const uint32_t nvme_cse_iocs_nvm[256] = { +static const uint32_t nvme_cse_iocs_nvm_default[256] = { [NVME_CMD_FLUSH] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE_ZEROES] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, @@ -300,7 +297,7 @@ static const uint32_t nvme_cse_iocs_nvm[256] = { [NVME_CMD_IO_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, }; -static const uint32_t nvme_cse_iocs_zoned[256] = { +static const uint32_t nvme_cse_iocs_zoned_default[256] = { [NVME_CMD_FLUSH] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE_ZEROES] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_WRITE] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, @@ -309,6 +306,9 @@ static const uint32_t nvme_cse_iocs_zoned[256] = { [NVME_CMD_VERIFY] = NVME_CMD_EFF_CSUPP, [NVME_CMD_COPY] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_COMPARE] = NVME_CMD_EFF_CSUPP, + [NVME_CMD_IO_MGMT_RECV] = NVME_CMD_EFF_CSUPP, + [NVME_CMD_IO_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, + [NVME_CMD_ZONE_APPEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_ZONE_MGMT_SEND] = NVME_CMD_EFF_CSUPP | NVME_CMD_EFF_LBCC, [NVME_CMD_ZONE_MGMT_RECV] = NVME_CMD_EFF_CSUPP, @@ -1762,47 +1762,6 @@ static uint16_t nvme_check_dulbe(NvmeNamespace *ns, uint64_t slba, return NVME_SUCCESS; } -static void nvme_aio_err(NvmeRequest *req, int ret) -{ - uint16_t status = NVME_SUCCESS; - Error *local_err = NULL; - - switch (req->cmd.opcode) { - case NVME_CMD_READ: - status = NVME_UNRECOVERED_READ; - break; - case NVME_CMD_FLUSH: - case NVME_CMD_WRITE: - case NVME_CMD_WRITE_ZEROES: - case NVME_CMD_ZONE_APPEND: - case NVME_CMD_COPY: - status = NVME_WRITE_FAULT; - break; - default: - status = NVME_INTERNAL_DEV_ERROR; - break; - } - - if (ret == -ECANCELED) { - status = NVME_CMD_ABORT_REQ; - } - - trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), status); - - error_setg_errno(&local_err, -ret, "aio failed"); - error_report_err(local_err); - - /* - * Set the command status code to the first encountered error but allow a - * subsequent Internal Device Error to trump it. - */ - if (req->status && status != NVME_INTERNAL_DEV_ERROR) { - return; - } - - req->status = status; -} - static inline uint32_t nvme_zone_idx(NvmeNamespace *ns, uint64_t slba) { return ns->zone_size_log2 > 0 ? slba >> ns->zone_size_log2 : @@ -2161,11 +2120,16 @@ static inline bool nvme_is_write(NvmeRequest *req) static void nvme_misc_cb(void *opaque, int ret) { NvmeRequest *req = opaque; + uint16_t cid = nvme_cid(req); - trace_pci_nvme_misc_cb(nvme_cid(req)); + trace_pci_nvme_misc_cb(cid); if (ret) { - nvme_aio_err(req, ret); + if (!req->status) { + req->status = NVME_INTERNAL_DEV_ERROR; + } + + trace_pci_nvme_err_aio(cid, strerror(-ret), req->status); } nvme_enqueue_req_completion(nvme_cq(req), req); @@ -2182,8 +2146,30 @@ void nvme_rw_complete_cb(void *opaque, int ret) trace_pci_nvme_rw_complete_cb(nvme_cid(req), blk_name(blk)); if (ret) { + Error *err = NULL; + block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + + switch (req->cmd.opcode) { + case NVME_CMD_READ: + req->status = NVME_UNRECOVERED_READ; + break; + + case NVME_CMD_WRITE: + case NVME_CMD_WRITE_ZEROES: + case NVME_CMD_ZONE_APPEND: + req->status = NVME_WRITE_FAULT; + break; + + default: + req->status = NVME_INTERNAL_DEV_ERROR; + break; + } + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + + error_setg_errno(&err, -ret, "aio failed"); + error_report_err(err); } else { block_acct_done(stats, acct); } @@ -2268,7 +2254,10 @@ static void nvme_verify_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2367,7 +2356,10 @@ static void nvme_compare_mdata_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2449,7 +2441,10 @@ static void nvme_compare_data_cb(void *opaque, int ret) if (ret) { block_acct_failed(stats, acct); - nvme_aio_err(req, ret); + req->status = NVME_UNRECOVERED_READ; + + trace_pci_nvme_err_aio(nvme_cid(req), strerror(-ret), req->status); + goto out; } @@ -2928,6 +2923,7 @@ static void nvme_copy_out_completed_cb(void *opaque, int ret) if (ret < 0) { iocb->ret = ret; + req->status = NVME_WRITE_FAULT; goto out; } else if (iocb->ret < 0) { goto out; @@ -2992,6 +2988,7 @@ static void nvme_copy_in_completed_cb(void *opaque, int ret) if (ret < 0) { iocb->ret = ret; + req->status = NVME_UNRECOVERED_READ; goto out; } else if (iocb->ret < 0) { goto out; @@ -3514,6 +3511,7 @@ static void nvme_flush_ns_cb(void *opaque, int ret) if (ret < 0) { iocb->ret = ret; + iocb->req->status = NVME_WRITE_FAULT; goto out; } else if (iocb->ret < 0) { goto out; @@ -4605,6 +4603,61 @@ static uint16_t nvme_io_mgmt_send(NvmeCtrl *n, NvmeRequest *req) }; } +static uint16_t __nvme_io_cmd_nvm(NvmeCtrl *n, NvmeRequest *req) +{ + switch (req->cmd.opcode) { + case NVME_CMD_WRITE: + return nvme_write(n, req); + case NVME_CMD_READ: + return nvme_read(n, req); + case NVME_CMD_COMPARE: + return nvme_compare(n, req); + case NVME_CMD_WRITE_ZEROES: + return nvme_write_zeroes(n, req); + case NVME_CMD_DSM: + return nvme_dsm(n, req); + case NVME_CMD_VERIFY: + return nvme_verify(n, req); + case NVME_CMD_COPY: + return nvme_copy(n, req); + case NVME_CMD_IO_MGMT_RECV: + return nvme_io_mgmt_recv(n, req); + case NVME_CMD_IO_MGMT_SEND: + return nvme_io_mgmt_send(n, req); + } + + g_assert_not_reached(); +} + +static uint16_t nvme_io_cmd_nvm(NvmeCtrl *n, NvmeRequest *req) +{ + if (!(n->cse.iocs.nvm[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + trace_pci_nvme_err_invalid_opc(req->cmd.opcode); + return NVME_INVALID_OPCODE | NVME_DNR; + } + + return __nvme_io_cmd_nvm(n, req); +} + +static uint16_t nvme_io_cmd_zoned(NvmeCtrl *n, NvmeRequest *req) +{ + if (!(n->cse.iocs.zoned[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + trace_pci_nvme_err_invalid_opc(req->cmd.opcode); + return NVME_INVALID_OPCODE | NVME_DNR; + } + + switch (req->cmd.opcode) { + case NVME_CMD_ZONE_APPEND: + return nvme_zone_append(n, req); + case NVME_CMD_ZONE_MGMT_SEND: + return nvme_zone_mgmt_send(n, req); + case NVME_CMD_ZONE_MGMT_RECV: + return nvme_zone_mgmt_recv(n, req); + } + + return __nvme_io_cmd_nvm(n, req); +} + static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) { NvmeNamespace *ns; @@ -4646,11 +4699,6 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) return NVME_INVALID_FIELD | NVME_DNR; } - if (!(ns->iocs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { - trace_pci_nvme_err_invalid_opc(req->cmd.opcode); - return NVME_INVALID_OPCODE | NVME_DNR; - } - if (ns->status) { return ns->status; } @@ -4661,36 +4709,14 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeRequest *req) req->ns = ns; - switch (req->cmd.opcode) { - case NVME_CMD_WRITE_ZEROES: - return nvme_write_zeroes(n, req); - case NVME_CMD_ZONE_APPEND: - return nvme_zone_append(n, req); - case NVME_CMD_WRITE: - return nvme_write(n, req); - case NVME_CMD_READ: - return nvme_read(n, req); - case NVME_CMD_COMPARE: - return nvme_compare(n, req); - case NVME_CMD_DSM: - return nvme_dsm(n, req); - case NVME_CMD_VERIFY: - return nvme_verify(n, req); - case NVME_CMD_COPY: - return nvme_copy(n, req); - case NVME_CMD_ZONE_MGMT_SEND: - return nvme_zone_mgmt_send(n, req); - case NVME_CMD_ZONE_MGMT_RECV: - return nvme_zone_mgmt_recv(n, req); - case NVME_CMD_IO_MGMT_RECV: - return nvme_io_mgmt_recv(n, req); - case NVME_CMD_IO_MGMT_SEND: - return nvme_io_mgmt_send(n, req); - default: - g_assert_not_reached(); + switch (ns->csi) { + case NVME_CSI_NVM: + return nvme_io_cmd_nvm(n, req); + case NVME_CSI_ZONED: + return nvme_io_cmd_zoned(n, req); } - return NVME_INVALID_OPCODE | NVME_DNR; + g_assert_not_reached(); } static void nvme_cq_notifier(EventNotifier *e) @@ -4799,6 +4825,7 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeRequest *req) while (!QTAILQ_EMPTY(&sq->out_req_list)) { r = QTAILQ_FIRST(&sq->out_req_list); assert(r->aiocb); + r->status = NVME_CMD_ABORT_SQ_DEL; blk_aio_cancel(r->aiocb); } @@ -4917,6 +4944,45 @@ static void nvme_set_blk_stats(NvmeNamespace *ns, struct nvme_stats *stats) stats->write_commands += s->nr_ops[BLOCK_ACCT_WRITE]; } +static uint16_t nvme_ocp_extended_smart_info(NvmeCtrl *n, uint8_t rae, + uint32_t buf_len, uint64_t off, + NvmeRequest *req) +{ + NvmeNamespace *ns = NULL; + NvmeSmartLogExtended smart_l = { 0 }; + struct nvme_stats stats = { 0 }; + uint32_t trans_len; + + if (off >= sizeof(smart_l)) { + return NVME_INVALID_FIELD | NVME_DNR; + } + + /* accumulate all stats from all namespaces */ + for (int i = 1; i <= NVME_MAX_NAMESPACES; i++) { + ns = nvme_ns(n, i); + if (ns) { + nvme_set_blk_stats(ns, &stats); + } + } + + smart_l.physical_media_units_written[0] = cpu_to_le64(stats.units_written); + smart_l.physical_media_units_read[0] = cpu_to_le64(stats.units_read); + smart_l.log_page_version = 0x0005; + + static const uint8_t guid[16] = { + 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF, 0xF2, 0xA4, + 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF + }; + memcpy(smart_l.log_page_guid, guid, sizeof(smart_l.log_page_guid)); + + if (!rae) { + nvme_clear_events(n, NVME_AER_TYPE_SMART); + } + + trans_len = MIN(sizeof(smart_l) - off, buf_len); + return nvme_c2h(n, (uint8_t *) &smart_l + off, trans_len, req); +} + static uint16_t nvme_smart_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, uint64_t off, NvmeRequest *req) { @@ -5110,7 +5176,7 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, uint64_t off, NvmeRequest *req) { NvmeEffectsLog log = {}; - const uint32_t *src_iocs = NULL; + const uint32_t *iocs = NULL; uint32_t trans_len; if (off >= sizeof(log)) { @@ -5120,25 +5186,26 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, switch (NVME_CC_CSS(ldl_le_p(&n->bar.cc))) { case NVME_CC_CSS_NVM: - src_iocs = nvme_cse_iocs_nvm; - /* fall through */ - case NVME_CC_CSS_ADMIN_ONLY: + iocs = n->cse.iocs.nvm; break; - case NVME_CC_CSS_CSI: + + case NVME_CC_CSS_ALL: switch (csi) { case NVME_CSI_NVM: - src_iocs = nvme_cse_iocs_nvm; + iocs = n->cse.iocs.nvm; break; case NVME_CSI_ZONED: - src_iocs = nvme_cse_iocs_zoned; + iocs = n->cse.iocs.zoned; break; } + + break; } - memcpy(log.acs, nvme_cse_acs, sizeof(nvme_cse_acs)); + memcpy(log.acs, n->cse.acs, sizeof(log.acs)); - if (src_iocs) { - memcpy(log.iocs, src_iocs, sizeof(log.iocs)); + if (iocs) { + memcpy(log.iocs, iocs, sizeof(log.iocs)); } trans_len = MIN(sizeof(log) - off, buf_len); @@ -5146,6 +5213,23 @@ static uint16_t nvme_cmd_effects(NvmeCtrl *n, uint8_t csi, uint32_t buf_len, return nvme_c2h(n, ((uint8_t *)&log) + off, trans_len, req); } +static uint16_t nvme_vendor_specific_log(NvmeCtrl *n, uint8_t rae, + uint32_t buf_len, uint64_t off, + NvmeRequest *req, uint8_t lid) +{ + switch (lid) { + case NVME_OCP_EXTENDED_SMART_INFO: + if (n->params.ocp) { + return nvme_ocp_extended_smart_info(n, rae, buf_len, off, req); + } + break; + /* add a case for each additional vendor specific log id */ + } + + trace_pci_nvme_err_invalid_log_page(nvme_cid(req), lid); + return NVME_INVALID_FIELD | NVME_DNR; +} + static size_t sizeof_fdp_conf_descr(size_t nruh, size_t vss) { size_t entry_siz = sizeof(NvmeFdpDescrHdr) + nruh * sizeof(NvmeRuhDescr) @@ -5396,6 +5480,8 @@ static uint16_t nvme_get_log(NvmeCtrl *n, NvmeRequest *req) return nvme_smart_info(n, rae, len, off, req); case NVME_LOG_FW_SLOT_INFO: return nvme_fw_log_info(n, len, off, req); + case NVME_LOG_VENDOR_START...NVME_LOG_VENDOR_END: + return nvme_vendor_specific_log(n, rae, len, off, req, lid); case NVME_LOG_CHANGED_NSLIST: return nvme_changed_nslist(n, rae, len, off, req); case NVME_LOG_CMD_EFFECTS: @@ -5583,7 +5669,9 @@ static uint16_t nvme_identify_ctrl_csi(NvmeCtrl *n, NvmeRequest *req) switch (c->csi) { case NVME_CSI_NVM: id_nvm->vsl = n->params.vsl; + id_nvm->dmrl = NVME_ID_CTRL_NVM_DMRL_MAX; id_nvm->dmrsl = cpu_to_le32(n->dmrsl); + id_nvm->dmsl = NVME_ID_CTRL_NVM_DMRL_MAX * n->dmrsl; break; case NVME_CSI_ZONED: @@ -5625,7 +5713,7 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeRequest *req, bool active) return nvme_c2h(n, (uint8_t *)&ns->id_ns, sizeof(NvmeIdNs), req); } - return NVME_INVALID_CMD_SET | NVME_DNR; + return NVME_INVALID_IOCS | NVME_DNR; } static uint16_t nvme_identify_ctrl_list(NvmeCtrl *n, NvmeRequest *req, @@ -6048,6 +6136,7 @@ static uint16_t nvme_abort(NvmeCtrl *n, NvmeRequest *req) QTAILQ_FOREACH_SAFE(r, &sq->out_req_list, entry, next) { if (r->cqe.cid == cid) { if (r->aiocb) { + r->status = NVME_CMD_ABORT_REQ; blk_aio_cancel_async(r->aiocb); } break; @@ -6591,7 +6680,7 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeRequest *req) case NVME_COMMAND_SET_PROFILE: if (dw11 & 0x1ff) { trace_pci_nvme_err_invalid_iocsci(dw11 & 0x1ff); - return NVME_CMD_SET_CMB_REJECTED | NVME_DNR; + return NVME_IOCS_COMBINATION_REJECTED | NVME_DNR; } break; case NVME_FDP_MODE: @@ -6640,40 +6729,49 @@ static uint16_t nvme_aer(NvmeCtrl *n, NvmeRequest *req) return NVME_NO_COMPLETE; } -static void nvme_update_dmrsl(NvmeCtrl *n) +static void nvme_update_dsm_limits(NvmeCtrl *n, NvmeNamespace *ns) { - int nsid; + if (ns) { + n->dmrsl = + MIN_NON_ZERO(n->dmrsl, BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); + + return; + } - for (nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { - NvmeNamespace *ns = nvme_ns(n, nsid); + for (uint32_t nsid = 1; nsid <= NVME_MAX_NAMESPACES; nsid++) { + ns = nvme_ns(n, nsid); if (!ns) { continue; } - n->dmrsl = MIN_NON_ZERO(n->dmrsl, - BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); + n->dmrsl = + MIN_NON_ZERO(n->dmrsl, BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); } } -static void nvme_select_iocs_ns(NvmeCtrl *n, NvmeNamespace *ns) +static bool nvme_csi_supported(NvmeCtrl *n, uint8_t csi) { - uint32_t cc = ldl_le_p(&n->bar.cc); + uint32_t cc; - ns->iocs = nvme_cse_iocs_none; - switch (ns->csi) { + switch (csi) { case NVME_CSI_NVM: - if (NVME_CC_CSS(cc) != NVME_CC_CSS_ADMIN_ONLY) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; + return true; + case NVME_CSI_ZONED: - if (NVME_CC_CSS(cc) == NVME_CC_CSS_CSI) { - ns->iocs = nvme_cse_iocs_zoned; - } else if (NVME_CC_CSS(cc) == NVME_CC_CSS_NVM) { - ns->iocs = nvme_cse_iocs_nvm; - } - break; + cc = ldl_le_p(&n->bar.cc); + + return NVME_CC_CSS(cc) == NVME_CC_CSS_ALL; } + + g_assert_not_reached(); +} + +static void nvme_detach_ns(NvmeCtrl *n, NvmeNamespace *ns) +{ + assert(ns->attached > 0); + + n->namespaces[ns->params.nsid] = NULL; + ns->attached--; } static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) @@ -6718,7 +6816,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) switch (sel) { case NVME_NS_ATTACHMENT_ATTACH: - if (nvme_ns(ctrl, nsid)) { + if (nvme_ns(n, nsid)) { return NVME_NS_ALREADY_ATTACHED | NVME_DNR; } @@ -6726,20 +6824,18 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) return NVME_NS_PRIVATE | NVME_DNR; } + if (!nvme_csi_supported(n, ns->csi)) { + return NVME_IOCS_NOT_SUPPORTED | NVME_DNR; + } + nvme_attach_ns(ctrl, ns); - nvme_select_iocs_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, ns); break; case NVME_NS_ATTACHMENT_DETACH: - if (!nvme_ns(ctrl, nsid)) { - return NVME_NS_NOT_ATTACHED | NVME_DNR; - } - - ctrl->namespaces[nsid] = NULL; - ns->attached--; - - nvme_update_dmrsl(ctrl); + nvme_detach_ns(ctrl, ns); + nvme_update_dsm_limits(ctrl, NULL); break; @@ -7242,7 +7338,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeRequest *req) trace_pci_nvme_admin_cmd(nvme_cid(req), nvme_sqid(req), req->cmd.opcode, nvme_adm_opc_str(req->cmd.opcode)); - if (!(nvme_cse_acs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { + if (!(n->cse.acs[req->cmd.opcode] & NVME_CMD_EFF_CSUPP)) { trace_pci_nvme_err_invalid_admin_opc(req->cmd.opcode); return NVME_INVALID_OPCODE | NVME_DNR; } @@ -7589,21 +7685,6 @@ static void nvme_ctrl_shutdown(NvmeCtrl *n) } } -static void nvme_select_iocs(NvmeCtrl *n) -{ - NvmeNamespace *ns; - int i; - - for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - ns = nvme_ns(n, i); - if (!ns) { - continue; - } - - nvme_select_iocs_ns(n, ns); - } -} - static int nvme_start_ctrl(NvmeCtrl *n) { uint64_t cap = ldq_le_p(&n->bar.cap); @@ -7670,7 +7751,18 @@ static int nvme_start_ctrl(NvmeCtrl *n) nvme_set_timestamp(n, 0ULL); - nvme_select_iocs(n); + /* verify that the command sets of attached namespaces are supported */ + for (int i = 1; i <= NVME_MAX_NAMESPACES; i++) { + NvmeNamespace *ns = nvme_subsys_ns(n->subsys, i); + + if (ns && nvme_csi_supported(n, ns->csi) && !ns->params.detached) { + if (!ns->attached || ns->params.shared) { + nvme_attach_ns(n, ns); + } + } + } + + nvme_update_dsm_limits(n, NULL); return 0; } @@ -8682,6 +8774,12 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) uint64_t cap = ldq_le_p(&n->bar.cap); NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); uint32_t ctratt; + uint16_t oacs; + + memcpy(n->cse.acs, nvme_cse_acs_default, sizeof(n->cse.acs)); + memcpy(n->cse.iocs.nvm, nvme_cse_iocs_nvm_default, sizeof(n->cse.iocs.nvm)); + memcpy(n->cse.iocs.zoned, nvme_cse_iocs_zoned_default, + sizeof(n->cse.iocs.zoned)); id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); @@ -8712,9 +8810,23 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->mdts = n->params.mdts; id->ver = cpu_to_le32(NVME_SPEC_VER); - id->oacs = - cpu_to_le16(NVME_OACS_NS_MGMT | NVME_OACS_FORMAT | NVME_OACS_DBBUF | - NVME_OACS_DIRECTIVES); + + oacs = NVME_OACS_NMS | NVME_OACS_FORMAT | NVME_OACS_DIRECTIVES; + + if (n->params.dbcs) { + oacs |= NVME_OACS_DBCS; + + n->cse.acs[NVME_ADM_CMD_DBBUF_CONFIG] = NVME_CMD_EFF_CSUPP; + } + + if (n->params.sriov_max_vfs) { + oacs |= NVME_OACS_VMS; + + n->cse.acs[NVME_ADM_CMD_VIRT_MNGMT] = NVME_CMD_EFF_CSUPP; + } + + id->oacs = cpu_to_le16(oacs); + id->cntrltype = 0x1; /* @@ -8765,15 +8877,13 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->psd[0].enlat = cpu_to_le32(0x10); id->psd[0].exlat = cpu_to_le32(0x4); - if (n->subsys) { - id->cmic |= NVME_CMIC_MULTI_CTRL; - ctratt |= NVME_CTRATT_ENDGRPS; + id->cmic |= NVME_CMIC_MULTI_CTRL; + ctratt |= NVME_CTRATT_ENDGRPS; - id->endgidmax = cpu_to_le16(0x1); + id->endgidmax = cpu_to_le16(0x1); - if (n->subsys->endgrp.fdp.enabled) { - ctratt |= NVME_CTRATT_FDPS; - } + if (n->subsys->endgrp.fdp.enabled) { + ctratt |= NVME_CTRATT_FDPS; } id->ctratt = cpu_to_le32(ctratt); @@ -8781,9 +8891,8 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) NVME_CAP_SET_MQES(cap, n->params.mqes); NVME_CAP_SET_CQR(cap, 1); NVME_CAP_SET_TO(cap, 0xf); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_NVM); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_CSI_SUPP); - NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_ADMIN_ONLY); + NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_NCSS); + NVME_CAP_SET_CSS(cap, NVME_CAP_CSS_IOCSS); NVME_CAP_SET_MPSMAX(cap, 4); NVME_CAP_SET_CMBS(cap, n->params.cmb_size_mb ? 1 : 0); NVME_CAP_SET_PMRS(cap, n->pmr.dev ? 1 : 0); @@ -8802,7 +8911,15 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp) int cntlid; if (!n->subsys) { - return 0; + DeviceState *dev = qdev_new(TYPE_NVME_SUBSYS); + + qdev_prop_set_string(dev, "nqn", n->params.serial); + + if (!qdev_realize(dev, NULL, errp)) { + return -1; + } + + n->subsys = NVME_SUBSYS(dev); } cntlid = nvme_subsys_register_ctrl(n, errp); @@ -8822,9 +8939,6 @@ void nvme_attach_ns(NvmeCtrl *n, NvmeNamespace *ns) n->namespaces[nsid] = ns; ns->attached++; - - n->dmrsl = MIN_NON_ZERO(n->dmrsl, - BDRV_REQUEST_MAX_BYTES / nvme_l2b(ns, 1)); } static void nvme_realize(PCIDevice *pci_dev, Error **errp) @@ -8880,7 +8994,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) return; } - nvme_attach_ns(n, ns); + n->subsys->namespaces[ns->params.nsid] = ns; } } @@ -8892,17 +9006,15 @@ static void nvme_exit(PCIDevice *pci_dev) nvme_ctrl_reset(n, NVME_RESET_FUNCTION); - if (n->subsys) { - for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - ns = nvme_ns(n, i); - if (ns) { - ns->attached--; - } + for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { + ns = nvme_ns(n, i); + if (ns) { + ns->attached--; } - - nvme_subsys_unregister_ctrl(n->subsys, n); } + nvme_subsys_unregister_ctrl(n->subsys, n); + g_free(n->cq); g_free(n->sq); g_free(n->aer_reqs); @@ -8951,6 +9063,7 @@ static const Property nvme_props[] = { DEFINE_PROP_BOOL("use-intel-id", NvmeCtrl, params.use_intel_id, false), DEFINE_PROP_BOOL("legacy-cmb", NvmeCtrl, params.legacy_cmb, false), DEFINE_PROP_BOOL("ioeventfd", NvmeCtrl, params.ioeventfd, false), + DEFINE_PROP_BOOL("dbcs", NvmeCtrl, params.dbcs, true), DEFINE_PROP_UINT8("zoned.zasl", NvmeCtrl, params.zasl, 0), DEFINE_PROP_BOOL("zoned.auto_transition", NvmeCtrl, params.auto_transition_zones, true), @@ -8971,6 +9084,7 @@ static const Property nvme_props[] = { DEFINE_PROP_BOOL("atomic.dn", NvmeCtrl, params.atomic_dn, 0), DEFINE_PROP_UINT16("atomic.awun", NvmeCtrl, params.atomic_awun, 0), DEFINE_PROP_UINT16("atomic.awupf", NvmeCtrl, params.atomic_awupf, 0), + DEFINE_PROP_BOOL("ocp", NvmeCtrl, params.ocp, false), }; static void nvme_get_smart_warning(Object *obj, Visitor *v, const char *name, diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 410df29591..98c1e75a5d 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -727,25 +727,14 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) uint32_t nsid = ns->params.nsid; int i; - if (!n->subsys) { - /* If no subsys, the ns cannot be attached to more than one ctrl. */ - ns->params.shared = false; - if (ns->params.detached) { - error_setg(errp, "detached requires that the nvme device is " - "linked to an nvme-subsys device"); - return; - } - } else { - /* - * If this namespace belongs to a subsystem (through a link on the - * controller device), reparent the device. - */ - if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) { - return; - } - ns->subsys = subsys; - ns->endgrp = &subsys->endgrp; + assert(subsys); + + /* reparent to subsystem bus */ + if (!qdev_set_parent_bus(dev, &subsys->bus.parent_bus, errp)) { + return; } + ns->subsys = subsys; + ns->endgrp = &subsys->endgrp; if (nvme_ns_setup(ns, errp)) { return; @@ -753,7 +742,7 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) if (!nsid) { for (i = 1; i <= NVME_MAX_NAMESPACES; i++) { - if (nvme_ns(n, i) || nvme_subsys_ns(subsys, i)) { + if (nvme_subsys_ns(subsys, i)) { continue; } @@ -765,38 +754,15 @@ static void nvme_ns_realize(DeviceState *dev, Error **errp) error_setg(errp, "no free namespace id"); return; } - } else { - if (nvme_ns(n, nsid) || nvme_subsys_ns(subsys, nsid)) { - error_setg(errp, "namespace id '%d' already allocated", nsid); - return; - } + } else if (nvme_subsys_ns(subsys, nsid)) { + error_setg(errp, "namespace id '%d' already allocated", nsid); + return; } - if (subsys) { - subsys->namespaces[nsid] = ns; - - ns->id_ns.endgid = cpu_to_le16(0x1); - ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); - - if (ns->params.detached) { - return; - } - - if (ns->params.shared) { - for (i = 0; i < ARRAY_SIZE(subsys->ctrls); i++) { - NvmeCtrl *ctrl = subsys->ctrls[i]; - - if (ctrl && ctrl != SUBSYS_SLOT_RSVD) { - nvme_attach_ns(ctrl, ns); - } - } - - return; - } - - } + subsys->namespaces[nsid] = ns; - nvme_attach_ns(n, ns); + ns->id_ns.endgid = cpu_to_le16(0x1); + ns->id_ns_ind.endgrpid = cpu_to_le16(0x1); } static const Property nvme_ns_props[] = { diff --git a/hw/nvme/nvme.h b/hw/nvme/nvme.h index 7242206910..6f782ba188 100644 --- a/hw/nvme/nvme.h +++ b/hw/nvme/nvme.h @@ -237,7 +237,6 @@ typedef struct NvmeNamespace { NvmeLBAF lbaf; unsigned int nlbaf; size_t lbasz; - const uint32_t *iocs; uint8_t csi; uint16_t status; int attached; @@ -539,12 +538,14 @@ typedef struct NvmeParams { bool auto_transition_zones; bool legacy_cmb; bool ioeventfd; + bool dbcs; uint16_t sriov_max_vfs; uint16_t sriov_vq_flexible; uint16_t sriov_vi_flexible; uint32_t sriov_max_vq_per_vf; uint32_t sriov_max_vi_per_vf; bool msix_exclusive_bar; + bool ocp; struct { bool mem; @@ -584,6 +585,14 @@ typedef struct NvmeCtrl { bool dbbuf_enabled; struct { + uint32_t acs[256]; + struct { + uint32_t nvm[256]; + uint32_t zoned[256]; + } iocs; + } cse; + + struct { MemoryRegion mem; uint8_t *buf; bool cmse; diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c index 0f08df812d..0a5881be31 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -90,8 +90,8 @@ hwaddr openrisc_load_initrd(void *fdt, const char *filename, return start + size; } -uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, - uint64_t mem_size) +uint32_t openrisc_load_fdt(MachineState *ms, void *fdt, + hwaddr load_start, uint64_t mem_size) { uint32_t fdt_addr; int ret; @@ -109,7 +109,9 @@ uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, /* Should only fail if we've built a corrupted tree */ g_assert(ret == 0); /* copy in the device tree */ - qemu_fdt_dumpdtb(fdt, fdtsize); + + /* Save FDT for dumpdtb monitor command */ + ms->fdt = fdt; rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, &address_space_memory); diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index e0da4067ba..d9e0744922 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -354,7 +354,7 @@ static void openrisc_sim_init(MachineState *machine) machine->initrd_filename, load_addr, machine->ram_size); } - boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, + boot_info.fdt_addr = openrisc_load_fdt(machine, state->fdt, load_addr, machine->ram_size); } } diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 7b60bf8509..9afe407b00 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -540,7 +540,7 @@ static void openrisc_virt_init(MachineState *machine) machine->initrd_filename, load_addr, machine->ram_size); } - boot_info.fdt_addr = openrisc_load_fdt(state->fdt, load_addr, + boot_info.fdt_addr = openrisc_load_fdt(machine, state->fdt, load_addr, machine->ram_size); } } diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index c91880b237..35c0415242 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -99,6 +99,9 @@ config ASTRO bool select PCI +config PCI_EXPRESS_FSL_IMX8M_PHY + bool + config GT64120 bool select PCI diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index 3e8c36e6a7..c07740bfaa 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -55,6 +55,17 @@ #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C +static void designware_pcie_root_bus_class_init(ObjectClass *klass, void *data) +{ + BusClass *k = BUS_CLASS(klass); + + /* + * Designware has only a single root complex. Enforce the limit on the + * parent bus + */ + k->max_dev = 1; +} + static DesignwarePCIEHost * designware_pcie_root_to_host(DesignwarePCIERoot *root) { @@ -699,7 +710,7 @@ static void designware_pcie_host_realize(DeviceState *dev, Error **errp) &s->pci.memory, &s->pci.io, 0, 4, - TYPE_PCIE_BUS); + TYPE_DESIGNWARE_PCIE_ROOT_BUS); pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE; memory_region_init(&s->pci.address_space_root, @@ -754,6 +765,11 @@ static void designware_pcie_host_init(Object *obj) static const TypeInfo designware_pcie_types[] = { { + .name = TYPE_DESIGNWARE_PCIE_ROOT_BUS, + .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(DesignwarePCIERootBus), + .class_init = designware_pcie_root_bus_class_init, + }, { .name = TYPE_DESIGNWARE_PCIE_HOST, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(DesignwarePCIEHost), diff --git a/hw/pci-host/fsl_imx8m_phy.c b/hw/pci-host/fsl_imx8m_phy.c new file mode 100644 index 0000000000..aa304b102b --- /dev/null +++ b/hw/pci-host/fsl_imx8m_phy.c @@ -0,0 +1,98 @@ +/* + * i.MX8 PCIe PHY emulation + * + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/pci-host/fsl_imx8m_phy.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" + +#define CMN_REG075 0x1d4 +#define ANA_PLL_LOCK_DONE BIT(1) +#define ANA_PLL_AFC_DONE BIT(0) + +static uint64_t fsl_imx8m_pcie_phy_read(void *opaque, hwaddr offset, + unsigned size) +{ + FslImx8mPciePhyState *s = opaque; + + if (offset == CMN_REG075) { + return s->data[offset] | ANA_PLL_LOCK_DONE | ANA_PLL_AFC_DONE; + } + + return s->data[offset]; +} + +static void fsl_imx8m_pcie_phy_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + FslImx8mPciePhyState *s = opaque; + + s->data[offset] = value; +} + +static const MemoryRegionOps fsl_imx8m_pcie_phy_ops = { + .read = fsl_imx8m_pcie_phy_read, + .write = fsl_imx8m_pcie_phy_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void fsl_imx8m_pcie_phy_realize(DeviceState *dev, Error **errp) +{ + FslImx8mPciePhyState *s = FSL_IMX8M_PCIE_PHY(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &fsl_imx8m_pcie_phy_ops, s, + TYPE_FSL_IMX8M_PCIE_PHY, ARRAY_SIZE(s->data)); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static void fsl_imx8m_pcie_phy_reset_hold(Object *obj, ResetType type) +{ + FslImx8mPciePhyState *s = FSL_IMX8M_PCIE_PHY(obj); + + memset(s->data, 0, sizeof(s->data)); +} + +static const VMStateDescription fsl_imx8m_pcie_phy_vmstate = { + .name = "fsl-imx8m-pcie-phy", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(data, FslImx8mPciePhyState, + FSL_IMX8M_PCIE_PHY_DATA_SIZE), + VMSTATE_END_OF_LIST() + } +}; + +static void fsl_imx8m_pcie_phy_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->realize = fsl_imx8m_pcie_phy_realize; + dc->vmsd = &fsl_imx8m_pcie_phy_vmstate; + rc->phases.hold = fsl_imx8m_pcie_phy_reset_hold; +} + +static const TypeInfo fsl_imx8m_pcie_phy_types[] = { + { + .name = TYPE_FSL_IMX8M_PCIE_PHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(FslImx8mPciePhyState), + .class_init = fsl_imx8m_pcie_phy_class_init, + } +}; + +DEFINE_TYPES(fsl_imx8m_pcie_phy_types) diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index 3001e93a43..937a0f72ac 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -28,6 +28,7 @@ pci_ss.add(when: 'CONFIG_ARTICIA', if_true: files('articia.c')) pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c')) # ARM devices +pci_ss.add(when: 'CONFIG_PCI_EXPRESS_FSL_IMX8M_PHY', if_true: files('fsl_imx8m_phy.c')) pci_ss.add(when: 'CONFIG_VERSATILE_PCI', if_true: files('versatile.c')) # HPPA devices diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 26933e0457..fe8b9f7962 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -658,7 +658,6 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, done: if (!dry_run) { - qemu_fdt_dumpdtb(fdt, fdt_size); cpu_physical_memory_write(addr, fdt, fdt_size); /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b057672e82..246d6d633b 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -417,7 +417,6 @@ static void pegasos2_machine_reset(MachineState *machine, ResetType type) d[1] = cpu_to_be64(pm->kernel_size - (pm->kernel_entry - pm->kernel_addr)); qemu_fdt_setprop(fdt, "/chosen", "qemu,boot-kernel", d, sizeof(d)); - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); g_free(pm->fdt_blob); pm->fdt_blob = fdt; diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 11fd477b71..87607508c7 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -744,7 +744,6 @@ static void pnv_reset(MachineState *machine, ResetType type) _FDT((fdt_pack(fdt))); } - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); /* Update machine->fdt with latest fdt */ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f3a4b4235d..c15340a58d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1760,7 +1760,6 @@ static void spapr_machine_reset(MachineState *machine, ResetType type) 0, fdt_addr, 0); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); } - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); g_free(spapr->fdt_blob); spapr->fdt_size = fdt_totalsize(fdt); diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index c309441b7d..765b9e2b1a 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -374,8 +374,6 @@ void riscv_load_fdt(hwaddr fdt_addr, void *fdt) uint32_t fdtsize = fdt_totalsize(fdt); /* copy in the device tree */ - qemu_fdt_dumpdtb(fdt, fdtsize); - rom_add_blob_fixed_as("fdt", fdt, fdtsize, fdt_addr, &address_space_memory); qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds, diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index dcff18a987..ccb97b6806 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -77,6 +77,7 @@ struct HPETState { uint8_t rtc_irq_level; qemu_irq pit_enabled; uint8_t num_timers; + uint8_t num_timers_save; uint32_t intcap; HPETTimer timer[HPET_MAX_TIMERS]; @@ -237,15 +238,12 @@ static int hpet_pre_save(void *opaque) s->hpet_counter = hpet_get_ticks(s); } - return 0; -} - -static int hpet_pre_load(void *opaque) -{ - HPETState *s = opaque; - - /* version 1 only supports 3, later versions will load the actual value */ - s->num_timers = HPET_MIN_TIMERS; + /* + * The number of timers must match on source and destination, but it was + * also added to the migration stream. Check that it matches the value + * that was configured. + */ + s->num_timers_save = s->num_timers; return 0; } @@ -253,12 +251,7 @@ static bool hpet_validate_num_timers(void *opaque, int version_id) { HPETState *s = opaque; - if (s->num_timers < HPET_MIN_TIMERS) { - return false; - } else if (s->num_timers > HPET_MAX_TIMERS) { - return false; - } - return true; + return s->num_timers == s->num_timers_save; } static int hpet_post_load(void *opaque, int version_id) @@ -277,16 +270,6 @@ static int hpet_post_load(void *opaque, int version_id) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } - /* Push number of timers into capability returned via HPET_ID */ - s->capability &= ~HPET_ID_NUM_TIM_MASK; - s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; - hpet_fw_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; - - /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ - s->flags &= ~(1 << HPET_MSI_SUPPORT); - if (s->timer[0].config & HPET_TN_FSB_CAP) { - s->flags |= 1 << HPET_MSI_SUPPORT; - } return 0; } @@ -347,14 +330,13 @@ static const VMStateDescription vmstate_hpet = { .version_id = 2, .minimum_version_id = 1, .pre_save = hpet_pre_save, - .pre_load = hpet_pre_load, .post_load = hpet_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64(config, HPETState), VMSTATE_UINT64(isr, HPETState), VMSTATE_UINT64(hpet_counter, HPETState), - VMSTATE_UINT8_V(num_timers, HPETState, 2), - VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers), + VMSTATE_UINT8_V(num_timers_save, HPETState, 2), + VMSTATE_VALIDATE("num_timers must match", hpet_validate_num_timers), VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 11eca9fa4d..200a89225b 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -126,6 +126,17 @@ static const IMXClk imx7_gpt_clocks[] = { CLK_NONE, /* 111 not defined */ }; +static const IMXClk imx8mp_gpt_clocks[] = { + CLK_NONE, /* 000 No clock source */ + CLK_IPG, /* 001 ipg_clk, 532MHz */ + CLK_IPG_HIGH, /* 010 ipg_clk_highfreq */ + CLK_EXT, /* 011 External clock */ + CLK_32k, /* 100 ipg_clk_32k */ + CLK_HIGH, /* 101 ipg_clk_16M */ + CLK_NONE, /* 110 not defined */ + CLK_NONE, /* 111 not defined */ +}; + /* Must be called from within ptimer_transaction_begin/commit block */ static void imx_gpt_set_freq(IMXGPTState *s) { @@ -552,6 +563,13 @@ static void imx7_gpt_init(Object *obj) s->clocks = imx7_gpt_clocks; } +static void imx8mp_gpt_init(Object *obj) +{ + IMXGPTState *s = IMX_GPT(obj); + + s->clocks = imx8mp_gpt_clocks; +} + static const TypeInfo imx25_gpt_info = { .name = TYPE_IMX25_GPT, .parent = TYPE_SYS_BUS_DEVICE, @@ -584,6 +602,12 @@ static const TypeInfo imx7_gpt_info = { .instance_init = imx7_gpt_init, }; +static const TypeInfo imx8mp_gpt_info = { + .name = TYPE_IMX8MP_GPT, + .parent = TYPE_IMX25_GPT, + .instance_init = imx8mp_gpt_init, +}; + static void imx_gpt_register_types(void) { type_register_static(&imx25_gpt_info); @@ -591,6 +615,7 @@ static void imx_gpt_register_types(void) type_register_static(&imx6_gpt_info); type_register_static(&imx6ul_gpt_info); type_register_static(&imx7_gpt_info); + type_register_static(&imx8mp_gpt_info); } type_init(imx_gpt_register_types) diff --git a/hw/usb/hcd-dwc3.c b/hw/usb/hcd-dwc3.c index 9ce9ba0b04..0bceee2712 100644 --- a/hw/usb/hcd-dwc3.c +++ b/hw/usb/hcd-dwc3.c @@ -343,6 +343,8 @@ REG32(GFLADJ, 0x530) FIELD(GFLADJ, GFLADJ_REFCLK_FLADJ, 8, 14) FIELD(GFLADJ, GFLADJ_30MHZ_SDBND_SEL, 7, 1) FIELD(GFLADJ, GFLADJ_30MHZ, 0, 6) +REG32(GUSB2RHBCTL, 0x540) + FIELD(GUSB2RHBCTL, OVRD_L1TIMEOUT, 0, 4) #define DWC3_GLOBAL_OFFSET 0xC100 static void reset_csr(USBDWC3 * s) @@ -560,6 +562,9 @@ static const RegisterAccessInfo usb_dwc3_regs_info[] = { .rsvd = 0x40, .ro = 0x400040, .unimp = 0xffffffff, + },{ .name = "GUSB2RHBCTL", .addr = A_GUSB2RHBCTL, + .rsvd = 0xfffffff0, + .unimp = 0xffffffff, } }; diff --git a/include/block/nvme.h b/include/block/nvme.h index f4d108841b..358e516e38 100644 --- a/include/block/nvme.h +++ b/include/block/nvme.h @@ -142,9 +142,9 @@ enum NvmeCapMask { ((cap) |= (uint64_t)((val) & CAP_CMBS_MASK) << CAP_CMBS_SHIFT) enum NvmeCapCss { - NVME_CAP_CSS_NVM = 1 << 0, - NVME_CAP_CSS_CSI_SUPP = 1 << 6, - NVME_CAP_CSS_ADMIN_ONLY = 1 << 7, + NVME_CAP_CSS_NCSS = 1 << 0, + NVME_CAP_CSS_IOCSS = 1 << 6, + NVME_CAP_CSS_NOIOCSS = 1 << 7, }; enum NvmeCcShift { @@ -177,7 +177,7 @@ enum NvmeCcMask { enum NvmeCcCss { NVME_CC_CSS_NVM = 0x0, - NVME_CC_CSS_CSI = 0x6, + NVME_CC_CSS_ALL = 0x6, NVME_CC_CSS_ADMIN_ONLY = 0x7, }; @@ -906,8 +906,7 @@ enum NvmeStatusCodes { NVME_SGL_DESCR_TYPE_INVALID = 0x0011, NVME_INVALID_USE_OF_CMB = 0x0012, NVME_INVALID_PRP_OFFSET = 0x0013, - NVME_CMD_SET_CMB_REJECTED = 0x002b, - NVME_INVALID_CMD_SET = 0x002c, + NVME_COMMAND_INTERRUPTED = 0x0021, NVME_FDP_DISABLED = 0x0029, NVME_INVALID_PHID_LIST = 0x002a, NVME_LBA_RANGE = 0x0080, @@ -940,6 +939,10 @@ enum NvmeStatusCodes { NVME_INVALID_SEC_CTRL_STATE = 0x0120, NVME_INVALID_NUM_RESOURCES = 0x0121, NVME_INVALID_RESOURCE_ID = 0x0122, + NVME_IOCS_NOT_SUPPORTED = 0x0129, + NVME_IOCS_NOT_ENABLED = 0x012a, + NVME_IOCS_COMBINATION_REJECTED = 0x012b, + NVME_INVALID_IOCS = 0x012c, NVME_CONFLICTING_ATTRS = 0x0180, NVME_INVALID_PROT_INFO = 0x0181, NVME_WRITE_TO_RO = 0x0182, @@ -1015,6 +1018,40 @@ typedef struct QEMU_PACKED NvmeSmartLog { uint8_t reserved2[320]; } NvmeSmartLog; +typedef struct QEMU_PACKED NvmeSmartLogExtended { + uint64_t physical_media_units_written[2]; + uint64_t physical_media_units_read[2]; + uint64_t bad_user_blocks; + uint64_t bad_system_nand_blocks; + uint64_t xor_recovery_count; + uint64_t uncorrectable_read_error_count; + uint64_t soft_ecc_error_count; + uint64_t end2end_correction_counts; + uint8_t system_data_percent_used; + uint8_t refresh_counts[7]; + uint64_t user_data_erase_counts; + uint16_t thermal_throttling_stat_and_count; + uint16_t dssd_spec_version[3]; + uint64_t pcie_correctable_error_count; + uint32_t incomplete_shutdowns; + uint32_t rsvd116; + uint8_t percent_free_blocks; + uint8_t rsvd121[7]; + uint16_t capacity_health; + uint8_t nvme_errata_ver; + uint8_t rsvd131[5]; + uint64_t unaligned_io; + uint64_t security_ver_num; + uint64_t total_nuse; + uint64_t plp_start_count[2]; + uint64_t endurance_estimate[2]; + uint64_t pcie_retraining_count; + uint64_t power_state_change_count; + uint8_t rsvd208[286]; + uint16_t log_page_version; + uint64_t log_page_guid[2]; +} NvmeSmartLogExtended; + #define NVME_SMART_WARN_MAX 6 enum NvmeSmartWarn { NVME_SMART_SPARE = 1 << 0, @@ -1052,6 +1089,12 @@ enum NvmeLogIdentifier { NVME_LOG_FDP_RUH_USAGE = 0x21, NVME_LOG_FDP_STATS = 0x22, NVME_LOG_FDP_EVENTS = 0x23, + NVME_LOG_VENDOR_START = 0xc0, + NVME_LOG_VENDOR_END = 0xff, +}; + +enum NvmeOcpLogIdentifier { + NVME_OCP_EXTENDED_SMART_INFO = 0xc0, }; typedef struct QEMU_PACKED NvmePSD { @@ -1167,6 +1210,8 @@ typedef struct NvmeIdCtrlZoned { uint8_t rsvd1[4095]; } NvmeIdCtrlZoned; +#define NVME_ID_CTRL_NVM_DMRL_MAX 255 + typedef struct NvmeIdCtrlNvm { uint8_t vsl; uint8_t wzsl; @@ -1192,9 +1237,10 @@ enum NvmeIdCtrlOacs { NVME_OACS_SECURITY = 1 << 0, NVME_OACS_FORMAT = 1 << 1, NVME_OACS_FW = 1 << 2, - NVME_OACS_NS_MGMT = 1 << 3, + NVME_OACS_NMS = 1 << 3, NVME_OACS_DIRECTIVES = 1 << 5, - NVME_OACS_DBBUF = 1 << 8, + NVME_OACS_VMS = 1 << 7, + NVME_OACS_DBCS = 1 << 8, }; enum NvmeIdCtrlOncs { @@ -1899,6 +1945,7 @@ static inline void _nvme_check_size(void) QEMU_BUILD_BUG_ON(sizeof(NvmeErrorLog) != 64); QEMU_BUILD_BUG_ON(sizeof(NvmeFwSlotInfoLog) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLog) != 512); + QEMU_BUILD_BUG_ON(sizeof(NvmeSmartLogExtended) != 512); QEMU_BUILD_BUG_ON(sizeof(NvmeEffectsLog) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrl) != 4096); QEMU_BUILD_BUG_ON(sizeof(NvmeIdCtrlZoned) != 4096); diff --git a/include/fpu/softfloat-helpers.h b/include/fpu/softfloat-helpers.h index 8983c2748e..90862f5cd2 100644 --- a/include/fpu/softfloat-helpers.h +++ b/include/fpu/softfloat-helpers.h @@ -75,6 +75,12 @@ static inline void set_floatx80_rounding_precision(FloatX80RoundPrec val, status->floatx80_rounding_precision = val; } +static inline void set_floatx80_behaviour(FloatX80Behaviour b, + float_status *status) +{ + status->floatx80_behaviour = b; +} + static inline void set_float_2nan_prop_rule(Float2NaNPropRule rule, float_status *status) { @@ -151,6 +157,12 @@ get_floatx80_rounding_precision(const float_status *status) return status->floatx80_rounding_precision; } +static inline FloatX80Behaviour +get_floatx80_behaviour(const float_status *status) +{ + return status->floatx80_behaviour; +} + static inline Float2NaNPropRule get_float_2nan_prop_rule(const float_status *status) { diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 53d5eb8521..1af2a0cb14 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -321,6 +321,56 @@ typedef enum __attribute__((__packed__)) { } FloatFTZDetection; /* + * floatx80 is primarily used by x86 and m68k, and there are + * differences in the handling, largely related to the explicit + * Integer bit which floatx80 has and the other float formats do not. + * These flag values allow specification of the target's requirements + * and can be ORed together to set floatx80_behaviour. + */ +typedef enum __attribute__((__packed__)) { + /* In the default Infinity value, is the Integer bit 0 ? */ + floatx80_default_inf_int_bit_is_zero = 1, + /* + * Are Pseudo-infinities (Inf with the Integer bit zero) valid? + * If so, floatx80_is_infinity() will return true for them. + * If not, floatx80_invalid_encoding will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_pseudo_inf_valid = 2, + /* + * Are Pseudo-NaNs (NaNs where the Integer bit is zero) valid? + * If not, floatx80_invalid_encoding() will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_pseudo_nan_valid = 4, + /* + * Are Unnormals (0 < exp < 0x7fff, Integer bit zero) valid? + * If not, floatx80_invalid_encoding() will return false for them, + * and using them as inputs to a float op will raise Invalid. + */ + floatx80_unnormal_valid = 8, + + /* + * If the exponent is 0 and the Integer bit is set, Intel call + * this a "pseudo-denormal"; x86 supports that only on input + * (treating them as denormals by ignoring the Integer bit). + * For m68k, the integer bit is considered validly part of the + * input value when the exponent is 0, and may be 0 or 1, + * giving extra range. They may also be generated as outputs. + * (The m68k manual actually calls these values part of the + * normalized number range, not the denormalized number range.) + * + * By default you get the Intel behaviour where the Integer + * bit is ignored; if this is set then the Integer bit value + * is honoured, m68k-style. + * + * Either way, floatx80_invalid_encoding() will always accept + * pseudo-denormals. + */ + floatx80_pseudo_denormal_valid = 16, +} FloatX80Behaviour; + +/* * Floating Point Status. Individual architectures may maintain * several versions of float_status for different functions. The * correct status for the operation is then passed by reference to @@ -331,6 +381,7 @@ typedef struct float_status { uint16_t float_exception_flags; FloatRoundMode float_rounding_mode; FloatX80RoundPrec floatx80_rounding_precision; + FloatX80Behaviour floatx80_behaviour; Float2NaNPropRule float_2nan_prop_rule; Float3NaNPropRule float_3nan_prop_rule; FloatInfZeroNaNRule float_infzeronan_rule; diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 09a40b4310..c18ab2cb60 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -960,7 +960,7 @@ float128 floatx80_to_float128(floatx80, float_status *status); /*---------------------------------------------------------------------------- | The pattern for an extended double-precision inf. *----------------------------------------------------------------------------*/ -extern const floatx80 floatx80_infinity; +floatx80 floatx80_default_inf(bool zSign, float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision operations. @@ -995,14 +995,19 @@ static inline floatx80 floatx80_chs(floatx80 a) return a; } -static inline bool floatx80_is_infinity(floatx80 a) +static inline bool floatx80_is_infinity(floatx80 a, float_status *status) { -#if defined(TARGET_M68K) - return (a.high & 0x7fff) == floatx80_infinity.high && !(a.low << 1); -#else - return (a.high & 0x7fff) == floatx80_infinity.high && - a.low == floatx80_infinity.low; -#endif + /* + * It's target-specific whether the Integer bit is permitted + * to be 0 in a valid Infinity value. (x86 says no, m68k says yes). + */ + bool intbit = a.low >> 63; + + if (!intbit && + !(status->floatx80_behaviour & floatx80_pseudo_inf_valid)) { + return false; + } + return (a.high & 0x7fff) == 0x7fff && !(a.low << 1); } static inline bool floatx80_is_neg(floatx80 a) @@ -1068,41 +1073,45 @@ static inline bool floatx80_unordered_quiet(floatx80 a, floatx80 b, /*---------------------------------------------------------------------------- | Return whether the given value is an invalid floatx80 encoding. -| Invalid floatx80 encodings arise when the integer bit is not set, but -| the exponent is not zero. The only times the integer bit is permitted to -| be zero is in subnormal numbers and the value zero. -| This includes what the Intel software developer's manual calls pseudo-NaNs, -| pseudo-infinities and un-normal numbers. It does not include -| pseudo-denormals, which must still be correctly handled as inputs even -| if they are never generated as outputs. +| Invalid floatx80 encodings may arise when the integer bit is not set +| correctly; this is target-specific. In Intel terminology the +| categories are: +| exp == 0, int = 0, mantissa == 0 : zeroes +| exp == 0, int = 0, mantissa != 0 : denormals +| exp == 0, int = 1 : pseudo-denormals +| 0 < exp < 0x7fff, int = 0 : unnormals +| 0 < exp < 0x7fff, int = 1 : normals +| exp == 0x7fff, int = 0, mantissa == 0 : pseudo-infinities +| exp == 0x7fff, int = 1, mantissa == 0 : infinities +| exp == 0x7fff, int = 0, mantissa != 0 : pseudo-NaNs +| exp == 0x7fff, int = 1, mantissa == 0 : NaNs +| +| The usual IEEE cases of zero, denormal, normal, inf and NaN are always valid. +| x87 permits as input also pseudo-denormals. +| m68k permits all those and also pseudo-infinities, pseudo-NaNs and unnormals. +| +| Since we don't have a target that handles floatx80 but prohibits +| pseudo-denormals in input, we don't currently have a floatx80_behaviour +| flag for that case, but instead always accept it. Conveniently this +| means that all cases with either exponent 0 or the integer bit set are +| valid for all targets. *----------------------------------------------------------------------------*/ -static inline bool floatx80_invalid_encoding(floatx80 a) -{ -#if defined(TARGET_M68K) - /*------------------------------------------------------------------------- - | With m68k, the explicit integer bit can be zero in the case of: - | - zeros (exp == 0, mantissa == 0) - | - denormalized numbers (exp == 0, mantissa != 0) - | - unnormalized numbers (exp != 0, exp < 0x7FFF) - | - infinities (exp == 0x7FFF, mantissa == 0) - | - not-a-numbers (exp == 0x7FFF, mantissa != 0) - | - | For infinities and NaNs, the explicit integer bit can be either one or - | zero. - | - | The IEEE 754 standard does not define a zero integer bit. Such a number - | is an unnormalized number. Hardware does not directly support - | denormalized and unnormalized numbers, but implicitly supports them by - | trapping them as unimplemented data types, allowing efficient conversion - | in software. - | - | See "M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL", - | "1.6 FLOATING-POINT DATA TYPES" - *------------------------------------------------------------------------*/ - return false; -#else - return (a.low & (1ULL << 63)) == 0 && (a.high & 0x7FFF) != 0; -#endif +static inline bool floatx80_invalid_encoding(floatx80 a, float_status *s) +{ + if ((a.low >> 63) || (a.high & 0x7fff) == 0) { + /* Anything with the Integer bit set or the exponent 0 is valid */ + return false; + } + + if ((a.high & 0x7fff) == 0x7fff) { + if (a.low) { + return !(s->floatx80_behaviour & floatx80_pseudo_nan_valid); + } else { + return !(s->floatx80_behaviour & floatx80_pseudo_inf_valid); + } + } else { + return !(s->floatx80_behaviour & floatx80_unnormal_valid); + } } #define floatx80_zero make_floatx80(0x0000, 0x0000000000000000LL) diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h new file mode 100644 index 0000000000..bc97fc416e --- /dev/null +++ b/include/hw/arm/fsl-imx8mp.h @@ -0,0 +1,284 @@ +/* + * i.MX 8M Plus SoC Definitions + * + * Copyright (c) 2024, Bernhard Beschow <shentey@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef FSL_IMX8MP_H +#define FSL_IMX8MP_H + +#include "cpu.h" +#include "hw/char/imx_serial.h" +#include "hw/gpio/imx_gpio.h" +#include "hw/i2c/imx_i2c.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/misc/imx7_snvs.h" +#include "hw/misc/imx8mp_analog.h" +#include "hw/misc/imx8mp_ccm.h" +#include "hw/net/imx_fec.h" +#include "hw/or-irq.h" +#include "hw/pci-host/designware.h" +#include "hw/pci-host/fsl_imx8m_phy.h" +#include "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" +#include "hw/timer/imx_gpt.h" +#include "hw/usb/hcd-dwc3.h" +#include "hw/watchdog/wdt_imx2.h" +#include "qom/object.h" +#include "qemu/units.h" + +#define TYPE_FSL_IMX8MP "fsl-imx8mp" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mpState, FSL_IMX8MP) + +#define FSL_IMX8MP_RAM_START 0x40000000 +#define FSL_IMX8MP_RAM_SIZE_MAX (8 * GiB) + +enum FslImx8mpConfiguration { + FSL_IMX8MP_NUM_CPUS = 4, + FSL_IMX8MP_NUM_ECSPIS = 3, + FSL_IMX8MP_NUM_GPIOS = 5, + FSL_IMX8MP_NUM_GPTS = 6, + FSL_IMX8MP_NUM_I2CS = 6, + FSL_IMX8MP_NUM_IRQS = 160, + FSL_IMX8MP_NUM_UARTS = 4, + FSL_IMX8MP_NUM_USBS = 2, + FSL_IMX8MP_NUM_USDHCS = 3, + FSL_IMX8MP_NUM_WDTS = 3, +}; + +struct FslImx8mpState { + DeviceState parent_obj; + + ARMCPU cpu[FSL_IMX8MP_NUM_CPUS]; + GICv3State gic; + IMXGPTState gpt[FSL_IMX8MP_NUM_GPTS]; + IMXGPIOState gpio[FSL_IMX8MP_NUM_GPIOS]; + IMX8MPCCMState ccm; + IMX8MPAnalogState analog; + IMX7SNVSState snvs; + IMXSPIState spi[FSL_IMX8MP_NUM_ECSPIS]; + IMXI2CState i2c[FSL_IMX8MP_NUM_I2CS]; + IMXSerialState uart[FSL_IMX8MP_NUM_UARTS]; + IMXFECState enet; + SDHCIState usdhc[FSL_IMX8MP_NUM_USDHCS]; + IMX2WdtState wdt[FSL_IMX8MP_NUM_WDTS]; + USBDWC3 usb[FSL_IMX8MP_NUM_USBS]; + DesignwarePCIEHost pcie; + FslImx8mPciePhyState pcie_phy; + OrIRQState gpt5_gpt6_irq; + MemoryRegion ocram; + + uint32_t phy_num; + bool phy_connected; +}; + +enum FslImx8mpMemoryRegions { + FSL_IMX8MP_A53_DAP, + FSL_IMX8MP_AIPS1_CONFIGURATION, + FSL_IMX8MP_AIPS2_CONFIGURATION, + FSL_IMX8MP_AIPS3_CONFIGURATION, + FSL_IMX8MP_AIPS4_CONFIGURATION, + FSL_IMX8MP_AIPS5_CONFIGURATION, + FSL_IMX8MP_ANA_OSC, + FSL_IMX8MP_ANA_PLL, + FSL_IMX8MP_ANA_TSENSOR, + FSL_IMX8MP_APBH_DMA, + FSL_IMX8MP_ASRC, + FSL_IMX8MP_AUDIO_BLK_CTRL, + FSL_IMX8MP_AUDIO_DSP, + FSL_IMX8MP_AUDIO_XCVR_RX, + FSL_IMX8MP_AUD_IRQ_STEER, + FSL_IMX8MP_BOOT_ROM, + FSL_IMX8MP_BOOT_ROM_PROTECTED, + FSL_IMX8MP_CAAM, + FSL_IMX8MP_CAAM_MEM, + FSL_IMX8MP_CCM, + FSL_IMX8MP_CSU, + FSL_IMX8MP_DDR_BLK_CTRL, + FSL_IMX8MP_DDR_CTL, + FSL_IMX8MP_DDR_PERF_MON, + FSL_IMX8MP_DDR_PHY, + FSL_IMX8MP_DDR_PHY_BROADCAST, + FSL_IMX8MP_ECSPI1, + FSL_IMX8MP_ECSPI2, + FSL_IMX8MP_ECSPI3, + FSL_IMX8MP_EDMA_CHANNELS, + FSL_IMX8MP_EDMA_MANAGEMENT_PAGE, + FSL_IMX8MP_ENET1, + FSL_IMX8MP_ENET2_TSN, + FSL_IMX8MP_FLEXCAN1, + FSL_IMX8MP_FLEXCAN2, + FSL_IMX8MP_GIC_DIST, + FSL_IMX8MP_GIC_REDIST, + FSL_IMX8MP_GPC, + FSL_IMX8MP_GPIO1, + FSL_IMX8MP_GPIO2, + FSL_IMX8MP_GPIO3, + FSL_IMX8MP_GPIO4, + FSL_IMX8MP_GPIO5, + FSL_IMX8MP_GPT1, + FSL_IMX8MP_GPT2, + FSL_IMX8MP_GPT3, + FSL_IMX8MP_GPT4, + FSL_IMX8MP_GPT5, + FSL_IMX8MP_GPT6, + FSL_IMX8MP_GPU2D, + FSL_IMX8MP_GPU3D, + FSL_IMX8MP_HDMI_TX, + FSL_IMX8MP_HDMI_TX_AUDLNK_MSTR, + FSL_IMX8MP_HSIO_BLK_CTL, + FSL_IMX8MP_I2C1, + FSL_IMX8MP_I2C2, + FSL_IMX8MP_I2C3, + FSL_IMX8MP_I2C4, + FSL_IMX8MP_I2C5, + FSL_IMX8MP_I2C6, + FSL_IMX8MP_INTERCONNECT, + FSL_IMX8MP_IOMUXC, + FSL_IMX8MP_IOMUXC_GPR, + FSL_IMX8MP_IPS_DEWARP, + FSL_IMX8MP_ISI, + FSL_IMX8MP_ISP1, + FSL_IMX8MP_ISP2, + FSL_IMX8MP_LCDIF1, + FSL_IMX8MP_LCDIF2, + FSL_IMX8MP_MEDIA_BLK_CTL, + FSL_IMX8MP_MIPI_CSI1, + FSL_IMX8MP_MIPI_CSI2, + FSL_IMX8MP_MIPI_DSI1, + FSL_IMX8MP_MU_1_A, + FSL_IMX8MP_MU_1_B, + FSL_IMX8MP_MU_2_A, + FSL_IMX8MP_MU_2_B, + FSL_IMX8MP_MU_3_A, + FSL_IMX8MP_MU_3_B, + FSL_IMX8MP_NPU, + FSL_IMX8MP_OCOTP_CTRL, + FSL_IMX8MP_OCRAM, + FSL_IMX8MP_OCRAM_S, + FSL_IMX8MP_PCIE1, + FSL_IMX8MP_PCIE1_MEM, + FSL_IMX8MP_PCIE_PHY1, + FSL_IMX8MP_PDM, + FSL_IMX8MP_PERFMON1, + FSL_IMX8MP_PERFMON2, + FSL_IMX8MP_PWM1, + FSL_IMX8MP_PWM2, + FSL_IMX8MP_PWM3, + FSL_IMX8MP_PWM4, + FSL_IMX8MP_QOSC, + FSL_IMX8MP_QSPI, + FSL_IMX8MP_QSPI1_RX_BUFFER, + FSL_IMX8MP_QSPI1_TX_BUFFER, + FSL_IMX8MP_QSPI_MEM, + FSL_IMX8MP_RAM, + FSL_IMX8MP_RDC, + FSL_IMX8MP_SAI1, + FSL_IMX8MP_SAI2, + FSL_IMX8MP_SAI3, + FSL_IMX8MP_SAI5, + FSL_IMX8MP_SAI6, + FSL_IMX8MP_SAI7, + FSL_IMX8MP_SDMA1, + FSL_IMX8MP_SDMA2, + FSL_IMX8MP_SDMA3, + FSL_IMX8MP_SEMAPHORE1, + FSL_IMX8MP_SEMAPHORE2, + FSL_IMX8MP_SEMAPHORE_HS, + FSL_IMX8MP_SNVS_HP, + FSL_IMX8MP_SPBA1, + FSL_IMX8MP_SPBA2, + FSL_IMX8MP_SRC, + FSL_IMX8MP_SYSCNT_CMP, + FSL_IMX8MP_SYSCNT_CTRL, + FSL_IMX8MP_SYSCNT_RD, + FSL_IMX8MP_TCM_DTCM, + FSL_IMX8MP_TCM_ITCM, + FSL_IMX8MP_TZASC, + FSL_IMX8MP_UART1, + FSL_IMX8MP_UART2, + FSL_IMX8MP_UART3, + FSL_IMX8MP_UART4, + FSL_IMX8MP_USB1, + FSL_IMX8MP_USB2, + FSL_IMX8MP_USB1_DEV, + FSL_IMX8MP_USB2_DEV, + FSL_IMX8MP_USB1_OTG, + FSL_IMX8MP_USB2_OTG, + FSL_IMX8MP_USB1_GLUE, + FSL_IMX8MP_USB2_GLUE, + FSL_IMX8MP_USDHC1, + FSL_IMX8MP_USDHC2, + FSL_IMX8MP_USDHC3, + FSL_IMX8MP_VPU, + FSL_IMX8MP_VPU_BLK_CTRL, + FSL_IMX8MP_VPU_G1_DECODER, + FSL_IMX8MP_VPU_G2_DECODER, + FSL_IMX8MP_VPU_VC8000E_ENCODER, + FSL_IMX8MP_WDOG1, + FSL_IMX8MP_WDOG2, + FSL_IMX8MP_WDOG3, +}; + +enum FslImx8mpIrqs { + FSL_IMX8MP_USDHC1_IRQ = 22, + FSL_IMX8MP_USDHC2_IRQ = 23, + FSL_IMX8MP_USDHC3_IRQ = 24, + + FSL_IMX8MP_UART1_IRQ = 26, + FSL_IMX8MP_UART2_IRQ = 27, + FSL_IMX8MP_UART3_IRQ = 28, + FSL_IMX8MP_UART4_IRQ = 29, + FSL_IMX8MP_UART5_IRQ = 30, + FSL_IMX8MP_UART6_IRQ = 16, + + FSL_IMX8MP_ECSPI1_IRQ = 31, + FSL_IMX8MP_ECSPI2_IRQ = 32, + FSL_IMX8MP_ECSPI3_IRQ = 33, + FSL_IMX8MP_ECSPI4_IRQ = 34, + + FSL_IMX8MP_I2C1_IRQ = 35, + FSL_IMX8MP_I2C2_IRQ = 36, + FSL_IMX8MP_I2C3_IRQ = 37, + FSL_IMX8MP_I2C4_IRQ = 38, + + FSL_IMX8MP_USB1_IRQ = 40, + FSL_IMX8MP_USB2_IRQ = 41, + + FSL_IMX8MP_GPT1_IRQ = 55, + FSL_IMX8MP_GPT2_IRQ = 54, + FSL_IMX8MP_GPT3_IRQ = 53, + FSL_IMX8MP_GPT4_IRQ = 52, + FSL_IMX8MP_GPT5_GPT6_IRQ = 51, + + FSL_IMX8MP_GPIO1_LOW_IRQ = 64, + FSL_IMX8MP_GPIO1_HIGH_IRQ = 65, + FSL_IMX8MP_GPIO2_LOW_IRQ = 66, + FSL_IMX8MP_GPIO2_HIGH_IRQ = 67, + FSL_IMX8MP_GPIO3_LOW_IRQ = 68, + FSL_IMX8MP_GPIO3_HIGH_IRQ = 69, + FSL_IMX8MP_GPIO4_LOW_IRQ = 70, + FSL_IMX8MP_GPIO4_HIGH_IRQ = 71, + FSL_IMX8MP_GPIO5_LOW_IRQ = 72, + FSL_IMX8MP_GPIO5_HIGH_IRQ = 73, + + FSL_IMX8MP_I2C5_IRQ = 76, + FSL_IMX8MP_I2C6_IRQ = 77, + + FSL_IMX8MP_WDOG1_IRQ = 78, + FSL_IMX8MP_WDOG2_IRQ = 79, + FSL_IMX8MP_WDOG3_IRQ = 10, + + FSL_IMX8MP_ENET1_MAC_IRQ = 118, + FSL_IMX6_ENET1_MAC_1588_IRQ = 121, + + FSL_IMX8MP_PCI_INTA_IRQ = 126, + FSL_IMX8MP_PCI_INTB_IRQ = 125, + FSL_IMX8MP_PCI_INTC_IRQ = 124, + FSL_IMX8MP_PCI_INTD_IRQ = 123, + FSL_IMX8MP_PCI_MSI_IRQ = 140, +}; + +#endif /* FSL_IMX8MP_H */ diff --git a/include/hw/boards.h b/include/hw/boards.h index 9360d1ce39..f22b2e7fc7 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -156,6 +156,8 @@ typedef struct { * @modules_supported - whether modules are supported by the machine * @cache_supported - whether cache (l1d, l1i, l2 and l3) configuration are * supported by the machine + * @has_caches - whether cache properties are explicitly specified in the + * user provided smp-cache configuration */ typedef struct { bool prefer_sockets; @@ -166,6 +168,7 @@ typedef struct { bool drawers_supported; bool modules_supported; bool cache_supported[CACHE_LEVEL_AND_TYPE__MAX]; + bool has_caches; } SMPCompatProps; /** diff --git a/include/hw/loader-fit.h b/include/hw/loader-fit.h index 0832e379dc..9a43490ed6 100644 --- a/include/hw/loader-fit.h +++ b/include/hw/loader-fit.h @@ -30,12 +30,27 @@ struct fit_loader_match { struct fit_loader { const struct fit_loader_match *matches; hwaddr (*addr_to_phys)(void *opaque, uint64_t addr); - const void *(*fdt_filter)(void *opaque, const void *fdt, - const void *match_data, hwaddr *load_addr); + void *(*fdt_filter)(void *opaque, const void *fdt, + const void *match_data, hwaddr *load_addr); const void *(*kernel_filter)(void *opaque, const void *kernel, hwaddr *load_addr, hwaddr *entry_addr); }; -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque); +/** + * load_fit: load a FIT format image + * @ldr: structure defining board specific properties and hooks + * @filename: image to load + * @pfdt: pointer to update with address of FDT blob + * @opaque: opaque value passed back to the hook functions in @ldr + * Returns: 0 on success, or a negative errno on failure + * + * @pfdt is used to tell the caller about the FDT blob. On return, it + * has been set to point to the FDT blob, and it is now the caller's + * responsibility to free that memory with g_free(). Usually the caller + * will want to pass in &machine->fdt here, to record the FDT blob for + * the dumpdtb option and QMP/HMP commands. + */ +int load_fit(const struct fit_loader *ldr, const char *filename, void **pfdt, + void *opaque); #endif /* HW_LOADER_FIT_H */ diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 661efae61d..2e7cdfaef0 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -68,4 +68,13 @@ struct LoongArchVirtMachineState { OBJECT_DECLARE_SIMPLE_TYPE(LoongArchVirtMachineState, LOONGARCH_VIRT_MACHINE) void virt_acpi_setup(LoongArchVirtMachineState *lvms); void virt_fdt_setup(LoongArchVirtMachineState *lvms); + +static inline bool virt_is_veiointc_enabled(LoongArchVirtMachineState *lvms) +{ + if (lvms->veiointc == ON_OFF_AUTO_OFF) { + return false; + } + return true; +} + #endif diff --git a/include/hw/misc/imx8mp_analog.h b/include/hw/misc/imx8mp_analog.h new file mode 100644 index 0000000000..955f03215a --- /dev/null +++ b/include/hw/misc/imx8mp_analog.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * i.MX8MP ANALOG IP block emulation code + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IMX8MP_ANALOG_H +#define IMX8MP_ANALOG_H + +#include "qom/object.h" +#include "hw/sysbus.h" + +enum IMX8MPAnalogRegisters { + ANALOG_AUDIO_PLL1_GEN_CTRL = 0x000 / 4, + ANALOG_AUDIO_PLL1_FDIV_CTL0 = 0x004 / 4, + ANALOG_AUDIO_PLL1_FDIV_CTL1 = 0x008 / 4, + ANALOG_AUDIO_PLL1_SSCG_CTRL = 0x00c / 4, + ANALOG_AUDIO_PLL1_MNIT_CTRL = 0x010 / 4, + ANALOG_AUDIO_PLL2_GEN_CTRL = 0x014 / 4, + ANALOG_AUDIO_PLL2_FDIV_CTL0 = 0x018 / 4, + ANALOG_AUDIO_PLL2_FDIV_CTL1 = 0x01c / 4, + ANALOG_AUDIO_PLL2_SSCG_CTRL = 0x020 / 4, + ANALOG_AUDIO_PLL2_MNIT_CTRL = 0x024 / 4, + ANALOG_VIDEO_PLL1_GEN_CTRL = 0x028 / 4, + ANALOG_VIDEO_PLL1_FDIV_CTL0 = 0x02c / 4, + ANALOG_VIDEO_PLL1_FDIV_CTL1 = 0x030 / 4, + ANALOG_VIDEO_PLL1_SSCG_CTRL = 0x034 / 4, + ANALOG_VIDEO_PLL1_MNIT_CTRL = 0x038 / 4, + ANALOG_DRAM_PLL_GEN_CTRL = 0x050 / 4, + ANALOG_DRAM_PLL_FDIV_CTL0 = 0x054 / 4, + ANALOG_DRAM_PLL_FDIV_CTL1 = 0x058 / 4, + ANALOG_DRAM_PLL_SSCG_CTRL = 0x05c / 4, + ANALOG_DRAM_PLL_MNIT_CTRL = 0x060 / 4, + ANALOG_GPU_PLL_GEN_CTRL = 0x064 / 4, + ANALOG_GPU_PLL_FDIV_CTL0 = 0x068 / 4, + ANALOG_GPU_PLL_LOCKD_CTRL = 0x06c / 4, + ANALOG_GPU_PLL_MNIT_CTRL = 0x070 / 4, + ANALOG_VPU_PLL_GEN_CTRL = 0x074 / 4, + ANALOG_VPU_PLL_FDIV_CTL0 = 0x078 / 4, + ANALOG_VPU_PLL_LOCKD_CTRL = 0x07c / 4, + ANALOG_VPU_PLL_MNIT_CTRL = 0x080 / 4, + ANALOG_ARM_PLL_GEN_CTRL = 0x084 / 4, + ANALOG_ARM_PLL_FDIV_CTL0 = 0x088 / 4, + ANALOG_ARM_PLL_LOCKD_CTRL = 0x08c / 4, + ANALOG_ARM_PLL_MNIT_CTRL = 0x090 / 4, + ANALOG_SYS_PLL1_GEN_CTRL = 0x094 / 4, + ANALOG_SYS_PLL1_FDIV_CTL0 = 0x098 / 4, + ANALOG_SYS_PLL1_LOCKD_CTRL = 0x09c / 4, + ANALOG_SYS_PLL1_MNIT_CTRL = 0x100 / 4, + ANALOG_SYS_PLL2_GEN_CTRL = 0x104 / 4, + ANALOG_SYS_PLL2_FDIV_CTL0 = 0x108 / 4, + ANALOG_SYS_PLL2_LOCKD_CTRL = 0x10c / 4, + ANALOG_SYS_PLL2_MNIT_CTRL = 0x110 / 4, + ANALOG_SYS_PLL3_GEN_CTRL = 0x114 / 4, + ANALOG_SYS_PLL3_FDIV_CTL0 = 0x118 / 4, + ANALOG_SYS_PLL3_LOCKD_CTRL = 0x11c / 4, + ANALOG_SYS_PLL3_MNIT_CTRL = 0x120 / 4, + ANALOG_OSC_MISC_CFG = 0x124 / 4, + ANALOG_ANAMIX_PLL_MNIT_CTL = 0x128 / 4, + + ANALOG_DIGPROG = 0x800 / 4, + ANALOG_MAX, +}; + +#define TYPE_IMX8MP_ANALOG "imx8mp.analog" +OBJECT_DECLARE_SIMPLE_TYPE(IMX8MPAnalogState, IMX8MP_ANALOG) + +struct IMX8MPAnalogState { + SysBusDevice parent_obj; + + struct { + MemoryRegion container; + MemoryRegion analog; + } mmio; + + uint32_t analog[ANALOG_MAX]; +}; + +#endif /* IMX8MP_ANALOG_H */ diff --git a/include/hw/misc/imx8mp_ccm.h b/include/hw/misc/imx8mp_ccm.h new file mode 100644 index 0000000000..685c8582ff --- /dev/null +++ b/include/hw/misc/imx8mp_ccm.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * i.MX 8M Plus CCM IP block emulation code + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef IMX8MP_CCM_H +#define IMX8MP_CCM_H + +#include "hw/misc/imx_ccm.h" +#include "qom/object.h" + +enum IMX8MPCCMRegisters { + CCM_MAX = 0xc6fc / sizeof(uint32_t) + 1, +}; + +#define TYPE_IMX8MP_CCM "imx8mp.ccm" +OBJECT_DECLARE_SIMPLE_TYPE(IMX8MPCCMState, IMX8MP_CCM) + +struct IMX8MPCCMState { + IMXCCMState parent_obj; + + MemoryRegion iomem; + + uint32_t ccm[CCM_MAX]; +}; + +#endif /* IMX8MP_CCM_H */ diff --git a/include/hw/openrisc/boot.h b/include/hw/openrisc/boot.h index 25a313d63a..9b4d88072c 100644 --- a/include/hw/openrisc/boot.h +++ b/include/hw/openrisc/boot.h @@ -20,6 +20,7 @@ #define OPENRISC_BOOT_H #include "exec/cpu-defs.h" +#include "hw/boards.h" hwaddr openrisc_load_kernel(ram_addr_t ram_size, const char *kernel_filename, @@ -28,7 +29,7 @@ hwaddr openrisc_load_kernel(ram_addr_t ram_size, hwaddr openrisc_load_initrd(void *fdt, const char *filename, hwaddr load_start, uint64_t mem_size); -uint32_t openrisc_load_fdt(void *fdt, hwaddr load_start, +uint32_t openrisc_load_fdt(MachineState *ms, void *fdt, hwaddr load_start, uint64_t mem_size); #endif /* OPENRISC_BOOT_H */ diff --git a/include/hw/pci-host/designware.h b/include/hw/pci-host/designware.h index bf8b278978..a35a3bd06c 100644 --- a/include/hw/pci-host/designware.h +++ b/include/hw/pci-host/designware.h @@ -25,12 +25,19 @@ #include "hw/pci/pci_bridge.h" #include "qom/object.h" +#define TYPE_DESIGNWARE_PCIE_ROOT_BUS "designware-pcie-root-BUS" +OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIERootBus, DESIGNWARE_PCIE_ROOT_BUS) + #define TYPE_DESIGNWARE_PCIE_HOST "designware-pcie-host" OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIEHost, DESIGNWARE_PCIE_HOST) #define TYPE_DESIGNWARE_PCIE_ROOT "designware-pcie-root" OBJECT_DECLARE_SIMPLE_TYPE(DesignwarePCIERoot, DESIGNWARE_PCIE_ROOT) +struct DesignwarePCIERootBus { + PCIBus parent; +}; + typedef struct DesignwarePCIEViewport { DesignwarePCIERoot *root; diff --git a/include/hw/pci-host/fsl_imx8m_phy.h b/include/hw/pci-host/fsl_imx8m_phy.h new file mode 100644 index 0000000000..4f4875b37d --- /dev/null +++ b/include/hw/pci-host/fsl_imx8m_phy.h @@ -0,0 +1,28 @@ +/* + * i.MX8 PCIe PHY emulation + * + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_PCIHOST_FSLIMX8MPCIEPHY_H +#define HW_PCIHOST_FSLIMX8MPCIEPHY_H + +#include "hw/sysbus.h" +#include "qom/object.h" +#include "exec/memory.h" + +#define TYPE_FSL_IMX8M_PCIE_PHY "fsl-imx8m-pcie-phy" +OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mPciePhyState, FSL_IMX8M_PCIE_PHY) + +#define FSL_IMX8M_PCIE_PHY_DATA_SIZE 0x800 + +struct FslImx8mPciePhyState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint8_t data[FSL_IMX8M_PCIE_PHY_DATA_SIZE]; +}; + +#endif diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h index 5a1230da35..5488f7e4df 100644 --- a/include/hw/timer/imx_gpt.h +++ b/include/hw/timer/imx_gpt.h @@ -80,6 +80,7 @@ #define TYPE_IMX6_GPT "imx6.gpt" #define TYPE_IMX6UL_GPT "imx6ul.gpt" #define TYPE_IMX7_GPT "imx7.gpt" +#define TYPE_IMX8MP_GPT "imx8mp.gpt" #define TYPE_IMX_GPT TYPE_IMX25_GPT diff --git a/include/hw/usb/hcd-dwc3.h b/include/hw/usb/hcd-dwc3.h index f752a27e94..dbdf12b21d 100644 --- a/include/hw/usb/hcd-dwc3.h +++ b/include/hw/usb/hcd-dwc3.h @@ -35,7 +35,7 @@ #define USB_DWC3(obj) \ OBJECT_CHECK(USBDWC3, (obj), TYPE_USB_DWC3) -#define USB_DWC3_R_MAX ((0x530 / 4) + 1) +#define USB_DWC3_R_MAX (0x600 / 4) #define DWC3_SIZE 0x10000 typedef struct USBDWC3 { diff --git a/include/system/device_tree.h b/include/system/device_tree.h index eb601522f8..49d8482ed4 100644 --- a/include/system/device_tree.h +++ b/include/system/device_tree.h @@ -133,8 +133,6 @@ int qemu_fdt_add_path(void *fdt, const char *path); sizeof(qdt_tmp)); \ } while (0) -void qemu_fdt_dumpdtb(void *fdt, int size); - /** * qemu_fdt_setprop_sized_cells_from_array: * @fdt: device tree blob diff --git a/meson.build b/meson.build index 0ee79c664d..0a2c61d2bf 100644 --- a/meson.build +++ b/meson.build @@ -821,7 +821,6 @@ version_res = [] coref = [] iokit = [] pvg = not_found -metal = [] emulator_link_args = [] midl = not_found widl = not_found @@ -843,8 +842,8 @@ elif host_os == 'darwin' coref = dependency('appleframeworks', modules: 'CoreFoundation') iokit = dependency('appleframeworks', modules: 'IOKit', required: false) host_dsosuf = '.dylib' - pvg = dependency('appleframeworks', modules: 'ParavirtualizedGraphics') - metal = dependency('appleframeworks', modules: 'Metal') + pvg = dependency('appleframeworks', modules: ['ParavirtualizedGraphics', 'Metal'], + required: get_option('pvg')) elif host_os == 'sunos' socket = [cc.find_library('socket'), cc.find_library('nsl'), @@ -3367,6 +3366,12 @@ foreach target : target_dirs target_kconfig += 'CONFIG_' + config_target['TARGET_ARCH'].to_upper() + '=y' target_kconfig += 'CONFIG_TARGET_BIG_ENDIAN=' + config_target['TARGET_BIG_ENDIAN'] + # PVG is not cross-architecture. Use accelerator_targets as a proxy to + # figure out which target can support PVG on this host + if pvg.found() and target in accelerator_targets.get('CONFIG_HVF', []) + target_kconfig += 'CONFIG_MAC_PVG=y' + endif + config_input = meson.get_external_property(target, 'default') config_devices_mak = target + '-config-devices.mak' config_devices_mak = configure_file( @@ -4840,6 +4845,9 @@ summary_info += {'libdw': libdw} if host_os == 'freebsd' summary_info += {'libinotify-kqueue': inotify} endif +if host_os == 'darwin' + summary_info += {'ParavirtualizedGraphics support': pvg} +endif summary(summary_info, bool_yn: true, section: 'Dependencies') if host_arch == 'unknown' diff --git a/meson_options.txt b/meson_options.txt index 5eeaf3eee5..59d973bca0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -198,6 +198,8 @@ option('lzfse', type : 'feature', value : 'auto', description: 'lzfse support for DMG images') option('lzo', type : 'feature', value : 'auto', description: 'lzo compression support') +option('pvg', type: 'feature', value: 'auto', + description: 'macOS paravirtualized graphics support') option('rbd', type : 'feature', value : 'auto', description: 'Ceph block device driver') option('opengl', type : 'feature', value : 'auto', diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 3825ff40a9..7ded3378cf 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -431,6 +431,6 @@ void hmp_dumpdtb(Monitor *mon, const QDict *qdict) return; } - monitor_printf(mon, "dtb dumped to %s", filename); + monitor_printf(mon, "DTB dumped to '%s'\n", filename); } #endif diff --git a/qemu-options.hx b/qemu-options.hx index 61270e3206..dc694a99a3 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -42,7 +42,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n" #endif " memory-backend='backend-id' specifies explicitly provided backend for main RAM (default=none)\n" - " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n", + " cxl-fmw.0.targets.0=firsttarget,cxl-fmw.0.targets.1=secondtarget,cxl-fmw.0.size=size[,cxl-fmw.0.interleave-granularity=granularity]\n" + " smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel\n", QEMU_ARCH_ALL) SRST ``-machine [type=]name[,prop=value[,...]]`` @@ -172,6 +173,33 @@ SRST :: -machine cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=128G,cxl-fmw.0.interleave-granularity=512 + + ``smp-cache.0.cache=cachename,smp-cache.0.topology=topologylevel`` + Define cache properties for SMP system. + + ``cache=cachename`` specifies the cache that the properties will be + applied on. This field is the combination of cache level and cache + type. It supports ``l1d`` (L1 data cache), ``l1i`` (L1 instruction + cache), ``l2`` (L2 unified cache) and ``l3`` (L3 unified cache). + + ``topology=topologylevel`` sets the cache topology level. It accepts + CPU topology levels including ``core``, ``module``, ``cluster``, ``die``, + ``socket``, ``book``, ``drawer`` and a special value ``default``. If + ``default`` is set, then the cache topology will follow the architecture's + default cache topology model. If another topology level is set, the cache + will be shared at corresponding CPU topology level. For example, + ``topology=core`` makes the cache shared by all threads within a core. + The omitting cache will default to using the ``default`` level. + + The default cache topology model for an i386 PC machine is as follows: + ``l1d``, ``l1i``, and ``l2`` caches are per ``core``, while the ``l3`` + cache is per ``die``. + + Example: + + :: + + -machine smp-cache.0.cache=l1d,smp-cache.0.topology=core,smp-cache.1.cache=l1i,smp-cache.1.topology=core ERST DEF("M", HAS_ARG, QEMU_OPTION_M, diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 6e3c15f539..12bc086d79 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -805,8 +805,10 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) int ret; ret = qmp_guest_fsfreeze_do_thaw(errp); + if (ret >= 0) { ga_unset_frozen(ga_state); + slog("guest-fsthaw called"); execute_fsfreeze_hook(FSFREEZE_HOOK_THAW, errp); } else { ret = 0; diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 99c026c0a0..749fdf8895 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1273,6 +1273,9 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) qga_vss_fsfreeze(&i, false, NULL, errp); ga_unset_frozen(ga_state); + + slog("guest-fsthaw called"); + return i; } diff --git a/qga/main.c b/qga/main.c index 531853e13d..72c39b042f 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1430,7 +1430,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) if (config->daemonize) { /* delay opening/locking of pidfile till filesystems are unfrozen */ s->deferred_options.pid_filepath = config->pid_filepath; - become_daemon(NULL); } if (config->log_filepath) { /* delay opening the log file till filesystems are unfrozen */ @@ -1438,9 +1437,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) } ga_disable_logging(s); } else { - if (config->daemonize) { - become_daemon(config->pid_filepath); - } if (config->log_filepath) { FILE *log_file = ga_open_logfile(config->log_filepath); if (!log_file) { @@ -1487,6 +1483,20 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) ga_apply_command_filters(s); + if (!channel_init(s, s->config->method, s->config->channel_path, + s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { + g_critical("failed to initialize guest agent channel"); + return NULL; + } + + if (config->daemonize) { + if (ga_is_frozen(s)) { + become_daemon(NULL); + } else { + become_daemon(config->pid_filepath); + } + } + ga_state = s; return s; } @@ -1513,8 +1523,9 @@ static void cleanup_agent(GAState *s) static int run_agent_once(GAState *s) { - if (!channel_init(s, s->config->method, s->config->channel_path, - s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { + if (!s->channel && + channel_init(s, s->config->method, s->config->channel_path, + s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { g_critical("failed to initialize guest agent channel"); return EXIT_FAILURE; } @@ -1523,6 +1534,7 @@ static int run_agent_once(GAState *s) if (s->channel) { ga_channel_free(s->channel); + s->channel = NULL; } return EXIT_SUCCESS; @@ -1579,7 +1591,7 @@ static void stop_agent(GAState *s, bool requested) int main(int argc, char **argv) { - int ret = EXIT_SUCCESS; + int ret = EXIT_FAILURE; GAState *s; GAConfig *config = g_new0(GAConfig, 1); int socket_activation; @@ -1607,7 +1619,6 @@ int main(int argc, char **argv) socket_activation = check_socket_activation(); if (socket_activation > 1) { g_critical("qemu-ga only supports listening on one socket"); - ret = EXIT_FAILURE; goto end; } if (socket_activation) { @@ -1631,7 +1642,6 @@ int main(int argc, char **argv) if (!config->method) { g_critical("unsupported listen fd type"); - ret = EXIT_FAILURE; goto end; } } else if (config->channel_path == NULL) { @@ -1643,13 +1653,13 @@ int main(int argc, char **argv) config->channel_path = g_strdup(QGA_SERIAL_PATH_DEFAULT); } else { g_critical("must specify a path for this channel"); - ret = EXIT_FAILURE; goto end; } } if (config->dumpconf) { config_dump(config); + ret = EXIT_SUCCESS; goto end; } @@ -1664,6 +1674,7 @@ int main(int argc, char **argv) SERVICE_TABLE_ENTRY service_table[] = { { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } }; StartServiceCtrlDispatcher(service_table); + ret = EXIT_SUCCESS; } else { ret = run_agent(s); } diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index f35d331331..1ffea1a728 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -4,9 +4,11 @@ #include "qapi/error.h" #include "qapi/qapi-visit-qom.h" #include "qobject/qobject.h" +#include "qobject/qbool.h" #include "qobject/qdict.h" #include "qapi/qmp/qerror.h" #include "qobject/qjson.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qom/object_interfaces.h" @@ -177,9 +179,25 @@ char *object_property_help(const char *name, const char *type, g_string_append(str, description); } if (defval) { - g_autofree char *def_json = g_string_free(qobject_to_json(defval), - false); - g_string_append_printf(str, " (default: %s)", def_json); + g_autofree char *def_json = NULL; + const char *def; + + switch (qobject_type(defval)) { + case QTYPE_QSTRING: + def = qstring_get_str(qobject_to(QString, defval)); + break; + + case QTYPE_QBOOL: + def = qbool_get_bool(qobject_to(QBool, defval)) ? "on" : "off"; + break; + + default: + def_json = g_string_free(qobject_to_json(defval), false); + def = def_json; + break; + } + + g_string_append_printf(str, " (default: %s)", def); } return g_string_free(str, false); diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 79e142723b..2ebf0a11ea 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -55,6 +55,12 @@ dependencies = [ ] [[package]] +name = "libc" +version = "0.2.162" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" + +[[package]] name = "pl011" version = "0.1.0" dependencies = [ @@ -100,6 +106,7 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ + "libc", "qemu_api_macros", "version_check", ] diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index fe73771021..d0857b470c 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,10 +2,10 @@ // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::{addr_of, addr_of_mut, NonNull}; use std::{ ffi::CStr, os::raw::{c_int, c_void}, + ptr::{addr_of, addr_of_mut, NonNull}, }; use qemu_api::{ @@ -19,8 +19,8 @@ use qemu_api::{ memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ClassInitImpl, ObjectImpl, Owned, ParentField}, - sysbus::{SysBusDevice, SysBusDeviceClass}, + qom::{ObjectImpl, Owned, ParentField}, + sysbus::{SysBusDevice, SysBusDeviceImpl}, vmstate::VMStateDescription, }; @@ -50,11 +50,6 @@ impl std::ops::Index<hwaddr> for DeviceId { } } -impl DeviceId { - const ARM: Self = Self(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); - const LUMINARY: Self = Self(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); -} - // FIFOs use 32-bit indices instead of usize, for compatibility with // the migration stream produced by the C version of this device. #[repr(transparent)] @@ -143,16 +138,24 @@ pub struct PL011Class { device_id: DeviceId, } +trait PL011Impl: SysBusDeviceImpl + IsA<PL011State> { + const DEVICE_ID: DeviceId; +} + +impl PL011Class { + fn class_init<T: PL011Impl>(&mut self) { + self.device_id = T::DEVICE_ID; + self.parent_class.class_init::<T>(); + } +} + unsafe impl ObjectType for PL011State { type Class = PL011Class; const TYPE_NAME: &'static CStr = crate::TYPE_PL011; } -impl ClassInitImpl<PL011Class> for PL011State { - fn class_init(klass: &mut PL011Class) { - klass.device_id = DeviceId::ARM; - <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); - } +impl PL011Impl for PL011State { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]); } impl ObjectImpl for PL011State { @@ -160,6 +163,7 @@ impl ObjectImpl for PL011State { const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init); const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>; } impl DeviceImpl for PL011State { @@ -176,6 +180,8 @@ impl ResettablePhasesImpl for PL011State { const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold); } +impl SysBusDeviceImpl for PL011State {} + impl PL011Registers { pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) { use RegisterOffset::*; @@ -726,13 +732,6 @@ pub struct PL011Luminary { parent_obj: ParentField<PL011State>, } -impl ClassInitImpl<PL011Class> for PL011Luminary { - fn class_init(klass: &mut PL011Class) { - klass.device_id = DeviceId::LUMINARY; - <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); - } -} - qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object); unsafe impl ObjectType for PL011Luminary { @@ -742,7 +741,14 @@ unsafe impl ObjectType for PL011Luminary { impl ObjectImpl for PL011Luminary { type ParentType = PL011State; + + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>; +} + +impl PL011Impl for PL011Luminary { + const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]); } impl DeviceImpl for PL011Luminary {} impl ResettablePhasesImpl for PL011Luminary {} +impl SysBusDeviceImpl for PL011Luminary {} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs index dbef93f6cb..0b2076ddaa 100644 --- a/rust/hw/char/pl011/src/device_class.rs +++ b/rust/hw/char/pl011/src/device_class.rs @@ -2,8 +2,10 @@ // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // SPDX-License-Identifier: GPL-2.0-or-later -use core::ptr::NonNull; -use std::os::raw::{c_int, c_void}; +use std::{ + os::raw::{c_int, c_void}, + ptr::NonNull, +}; use qemu_api::{ bindings::*, c_str, prelude::*, vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, diff --git a/rust/hw/timer/Kconfig b/rust/hw/timer/Kconfig index afd9803350..42e421317a 100644 --- a/rust/hw/timer/Kconfig +++ b/rust/hw/timer/Kconfig @@ -1,2 +1,3 @@ config X_HPET_RUST bool + default y if PC && HAVE_RUST diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 75ff5b3e8d..be27eb0eff 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -23,7 +23,7 @@ use qemu_api::{ qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl}, qom::{ObjectImpl, ObjectType, ParentField}, qom_isa, - sysbus::SysBusDevice, + sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL}, }; @@ -836,6 +836,7 @@ impl ObjectImpl for HPETState { const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init); const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init); + const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>; } // TODO: Make these properties user-configurable! @@ -887,3 +888,5 @@ impl DeviceImpl for HPETState { impl ResettablePhasesImpl for HPETState { const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold); } + +impl SysBusDeviceImpl for HPETState {} diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index a51dd14285..57747bc934 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -16,6 +16,7 @@ rust-version = "1.63.0" [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } +libc = "0.2.162" [build-dependencies] version_check = "~0.9" diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 2e9c1078b9..bcf1cf780f 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -2,6 +2,8 @@ _qemu_api_cfg = run_command(rustc_args, '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() +libc_dep = dependency('libc-0.2-rs') + # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if rustc.version().version_compare('>=1.77.0') _qemu_api_cfg += ['--cfg', 'has_offset_of'] @@ -22,6 +24,7 @@ _qemu_api_rs = static_library( 'src/cell.rs', 'src/chardev.rs', 'src/c_str.rs', + 'src/errno.rs', 'src/irq.rs', 'src/memory.rs', 'src/module.rs', @@ -39,6 +42,7 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, + dependencies: libc_dep, ) rust.test('rust-qemu-api-tests', _qemu_api_rs, diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index fa1a18de6f..104dec3977 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -92,3 +92,31 @@ macro_rules! assert_field_type { }; }; } + +/// Assert that an expression matches a pattern. This can also be +/// useful to compare enums that do not implement `Eq`. +/// +/// # Examples +/// +/// ``` +/// # use qemu_api::assert_match; +/// // JoinHandle does not implement `Eq`, therefore the result +/// // does not either. +/// let result: Result<std::thread::JoinHandle<()>, u32> = Err(42); +/// assert_match!(result, Err(42)); +/// ``` +#[macro_export] +macro_rules! assert_match { + ($a:expr, $b:pat) => { + assert!( + match $a { + $b => true, + _ => false, + }, + "{} = {:?} does not match {}", + stringify!($a), + $a, + stringify!($b) + ); + }; +} diff --git a/rust/qemu-api/src/errno.rs b/rust/qemu-api/src/errno.rs new file mode 100644 index 0000000000..18d101448b --- /dev/null +++ b/rust/qemu-api/src/errno.rs @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Utility functions to convert `errno` to and from +//! [`io::Error`]/[`io::Result`] +//! +//! QEMU C functions often have a "positive success/negative `errno`" calling +//! convention. This module provides functions to portably convert an integer +//! into an [`io::Result`] and back. + +use std::{convert::TryFrom, io, io::ErrorKind}; + +/// An `errno` value that can be converted into an [`io::Error`] +pub struct Errno(pub u16); + +// On Unix, from_raw_os_error takes an errno value and OS errors +// are printed using strerror. On Windows however it takes a +// GetLastError() value; therefore we need to convert errno values +// into io::Error by hand. This is the same mapping that the +// standard library uses to retrieve the kind of OS errors +// (`std::sys::pal::unix::decode_error_kind`). +impl From<Errno> for ErrorKind { + fn from(value: Errno) -> ErrorKind { + use ErrorKind::*; + let Errno(errno) = value; + match i32::from(errno) { + libc::EPERM | libc::EACCES => PermissionDenied, + libc::ENOENT => NotFound, + libc::EINTR => Interrupted, + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + libc::ENOMEM => OutOfMemory, + libc::EEXIST => AlreadyExists, + libc::EINVAL => InvalidInput, + libc::EPIPE => BrokenPipe, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::ENOTCONN => NotConnected, + libc::ENOTSUP => Unsupported, + libc::ETIMEDOUT => TimedOut, + _ => Other, + } + } +} + +// This is used on Windows for all io::Errors, but also on Unix if the +// io::Error does not have a raw OS error. This is the reversed +// mapping of the above; EIO is returned for unknown ErrorKinds. +impl From<io::ErrorKind> for Errno { + fn from(value: io::ErrorKind) -> Errno { + use ErrorKind::*; + let errno = match value { + // can be both EPERM or EACCES :( pick one + PermissionDenied => libc::EPERM, + NotFound => libc::ENOENT, + Interrupted => libc::EINTR, + WouldBlock => libc::EAGAIN, + OutOfMemory => libc::ENOMEM, + AlreadyExists => libc::EEXIST, + InvalidInput => libc::EINVAL, + BrokenPipe => libc::EPIPE, + AddrInUse => libc::EADDRINUSE, + AddrNotAvailable => libc::EADDRNOTAVAIL, + ConnectionAborted => libc::ECONNABORTED, + ConnectionRefused => libc::ECONNREFUSED, + ConnectionReset => libc::ECONNRESET, + NotConnected => libc::ENOTCONN, + Unsupported => libc::ENOTSUP, + TimedOut => libc::ETIMEDOUT, + _ => libc::EIO, + }; + Errno(errno as u16) + } +} + +impl From<Errno> for io::Error { + #[cfg(unix)] + fn from(value: Errno) -> io::Error { + let Errno(errno) = value; + io::Error::from_raw_os_error(errno.into()) + } + + #[cfg(windows)] + fn from(value: Errno) -> io::Error { + let error_kind: ErrorKind = value.into(); + error_kind.into() + } +} + +impl From<io::Error> for Errno { + fn from(value: io::Error) -> Errno { + if cfg!(unix) { + if let Some(errno) = value.raw_os_error() { + return Errno(u16::try_from(errno).unwrap()); + } + } + value.kind().into() + } +} + +/// Internal traits; used to enable [`into_io_result`] and [`into_neg_errno`] +/// for the "right" set of types. +mod traits { + use super::Errno; + + /// A signed type that can be converted into an + /// [`io::Result`](std::io::Result) + pub trait GetErrno { + /// Unsigned variant of `Self`, used as the type for the `Ok` case. + type Out; + + /// Return `Ok(self)` if positive, `Err(Errno(-self))` if negative + fn into_errno_result(self) -> Result<Self::Out, Errno>; + } + + /// A type that can be taken out of an [`io::Result`](std::io::Result) and + /// converted into "positive success/negative `errno`" convention. + pub trait MergeErrno { + /// Signed variant of `Self`, used as the return type of + /// [`into_neg_errno`](super::into_neg_errno). + type Out: From<u16> + std::ops::Neg<Output = Self::Out>; + + /// Return `self`, asserting that it is in range + fn map_ok(self) -> Self::Out; + } + + macro_rules! get_errno { + ($t:ty, $out:ty) => { + impl GetErrno for $t { + type Out = $out; + fn into_errno_result(self) -> Result<Self::Out, Errno> { + match self { + 0.. => Ok(self as $out), + -65535..=-1 => Err(Errno(-self as u16)), + _ => panic!("{self} is not a negative errno"), + } + } + } + }; + } + + get_errno!(i32, u32); + get_errno!(i64, u64); + get_errno!(isize, usize); + + macro_rules! merge_errno { + ($t:ty, $out:ty) => { + impl MergeErrno for $t { + type Out = $out; + fn map_ok(self) -> Self::Out { + self.try_into().unwrap() + } + } + }; + } + + merge_errno!(u8, i32); + merge_errno!(u16, i32); + merge_errno!(u32, i32); + merge_errno!(u64, i64); + + impl MergeErrno for () { + type Out = i32; + fn map_ok(self) -> i32 { + 0 + } + } +} + +use traits::{GetErrno, MergeErrno}; + +/// Convert an integer value into a [`io::Result`]. +/// +/// Positive values are turned into an `Ok` result; negative values +/// are interpreted as negated `errno` and turned into an `Err`. +/// +/// ``` +/// # use qemu_api::errno::into_io_result; +/// # use std::io::ErrorKind; +/// let ok = into_io_result(1i32).unwrap(); +/// assert_eq!(ok, 1u32); +/// +/// let err = into_io_result(-1i32).unwrap_err(); // -EPERM +/// assert_eq!(err.kind(), ErrorKind::PermissionDenied); +/// ``` +/// +/// # Panics +/// +/// Since the result is an unsigned integer, negative values must +/// be close to 0; values that are too far away are considered +/// likely overflows and will panic: +/// +/// ```should_panic +/// # use qemu_api::errno::into_io_result; +/// # #[allow(dead_code)] +/// let err = into_io_result(-0x1234_5678i32); // panic +/// ``` +pub fn into_io_result<T: GetErrno>(value: T) -> io::Result<T::Out> { + value.into_errno_result().map_err(Into::into) +} + +/// Convert a [`Result`] into an integer value, using negative `errno` +/// values to report errors. +/// +/// ``` +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io::{self, ErrorKind}; +/// let ok: io::Result<()> = Ok(()); +/// assert_eq!(into_neg_errno(ok), 0); +/// +/// let err: io::Result<()> = Err(ErrorKind::InvalidInput.into()); +/// assert_eq!(into_neg_errno(err), -22); // -EINVAL +/// ``` +/// +/// Since this module also provides the ability to convert [`io::Error`] +/// to an `errno` value, [`io::Result`] is the most commonly used type +/// for the argument of this function: +/// +/// # Panics +/// +/// Since the result is a signed integer, integer `Ok` values must remain +/// positive: +/// +/// ```should_panic +/// # use qemu_api::errno::into_neg_errno; +/// # use std::io; +/// let err: io::Result<u32> = Ok(0x8899_AABB); +/// into_neg_errno(err) // panic +/// # ; +/// ``` +pub fn into_neg_errno<T: MergeErrno, E: Into<Errno>>(value: Result<T, E>) -> T::Out { + match value { + Ok(x) => x.map_ok(), + Err(err) => -T::Out::from(err.into().0), + } +} + +#[cfg(test)] +mod tests { + use std::io::ErrorKind; + + use super::*; + use crate::assert_match; + + #[test] + pub fn test_from_u8() { + let ok: io::Result<_> = Ok(42u8); + assert_eq!(into_neg_errno(ok), 42); + + let err: io::Result<u8> = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result<u8> = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_from_u16() { + let ok: io::Result<_> = Ok(1234u16); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result<u16> = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result<u16> = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i32() { + assert_match!(into_io_result(1234i32), Ok(1234)); + + let err = into_io_result(-1i32).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(1)); + assert_match!(err.kind(), ErrorKind::PermissionDenied); + } + + #[test] + pub fn test_from_u32() { + let ok: io::Result<_> = Ok(1234u32); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result<u32> = Err(io::ErrorKind::PermissionDenied.into()); + assert_eq!(into_neg_errno(err), -1); + + if cfg!(unix) { + let os_err: io::Result<u32> = Err(io::Error::from_raw_os_error(10)); + assert_eq!(into_neg_errno(os_err), -10); + } + } + + #[test] + pub fn test_i64() { + assert_match!(into_io_result(1234i64), Ok(1234)); + + let err = into_io_result(-22i64).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(22)); + assert_match!(err.kind(), ErrorKind::InvalidInput); + } + + #[test] + pub fn test_from_u64() { + let ok: io::Result<_> = Ok(1234u64); + assert_eq!(into_neg_errno(ok), 1234); + + let err: io::Result<u64> = Err(io::ErrorKind::InvalidInput.into()); + assert_eq!(into_neg_errno(err), -22); + + if cfg!(unix) { + let os_err: io::Result<u64> = Err(io::Error::from_raw_os_error(6)); + assert_eq!(into_neg_errno(os_err), -6); + } + } + + #[test] + pub fn test_isize() { + assert_match!(into_io_result(1234isize), Ok(1234)); + + let err = into_io_result(-4isize).unwrap_err(); + #[cfg(unix)] + assert_match!(err.raw_os_error(), Some(4)); + assert_match!(err.kind(), ErrorKind::Interrupted); + } + + #[test] + pub fn test_from_unit() { + let ok: io::Result<_> = Ok(()); + assert_eq!(into_neg_errno(ok), 0); + + let err: io::Result<()> = Err(io::ErrorKind::OutOfMemory.into()); + assert_eq!(into_neg_errno(err), -12); + + if cfg!(unix) { + let os_err: io::Result<()> = Err(io::Error::from_raw_os_error(2)); + assert_eq!(into_neg_errno(os_err), -2); + } + } +} diff --git a/rust/qemu-api/src/irq.rs b/rust/qemu-api/src/irq.rs index d1c9dc96ef..34c19263c2 100644 --- a/rust/qemu-api/src/irq.rs +++ b/rust/qemu-api/src/irq.rs @@ -4,8 +4,7 @@ //! Bindings for interrupt sources -use core::ptr; -use std::{ffi::CStr, marker::PhantomData, os::raw::c_int}; +use std::{ffi::CStr, marker::PhantomData, os::raw::c_int, ptr}; use crate::{ bindings::{self, qemu_set_irq}, diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index ed1a8f9a2b..05f38b51d3 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -19,6 +19,7 @@ pub mod c_str; pub mod callbacks; pub mod cell; pub mod chardev; +pub mod errno; pub mod irq; pub mod memory; pub mod module; diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index fbf0ee23e0..634acf37a8 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -9,6 +9,8 @@ pub use crate::bitops::IntegerExt; pub use crate::cell::BqlCell; pub use crate::cell::BqlRefCell; +pub use crate::errno; + pub use crate::qdev::DeviceMethods; pub use crate::qom::InterfaceType; diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 3a7aa4def6..c136457090 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -19,7 +19,7 @@ use crate::{ chardev::Chardev, irq::InterruptSource, prelude::*, - qom::{ClassInitImpl, ObjectClass, ObjectImpl, Owned}, + qom::{ObjectClass, ObjectImpl, Owned}, vmstate::VMStateDescription, }; @@ -86,7 +86,7 @@ unsafe extern "C" fn rust_resettable_exit_fn<T: ResettablePhasesImpl>( } /// Trait providing the contents of [`DeviceClass`]. -pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl { +pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA<DeviceState> { /// _Realization_ is the second stage of device creation. It contains /// all operations that depend on device properties and can fail (note: /// this is not yet supported for Rust devices). @@ -113,7 +113,7 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl { /// # Safety /// /// This function is only called through the QOM machinery and -/// used by the `ClassInitImpl<DeviceClass>` trait. +/// used by `DeviceClass::class_init`. /// We expect the FFI user of this function to pass a valid pointer that /// can be downcasted to type `T`. We also expect the device is /// readable/writeable from one thread at any time. @@ -127,43 +127,41 @@ unsafe impl InterfaceType for ResettableClass { unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_RESETTABLE_INTERFACE) }; } -impl<T> ClassInitImpl<ResettableClass> for T -where - T: ResettablePhasesImpl, -{ - fn class_init(rc: &mut ResettableClass) { +impl ResettableClass { + /// Fill in the virtual methods of `ResettableClass` based on the + /// definitions in the `ResettablePhasesImpl` trait. + pub fn class_init<T: ResettablePhasesImpl>(&mut self) { if <T as ResettablePhasesImpl>::ENTER.is_some() { - rc.phases.enter = Some(rust_resettable_enter_fn::<T>); + self.phases.enter = Some(rust_resettable_enter_fn::<T>); } if <T as ResettablePhasesImpl>::HOLD.is_some() { - rc.phases.hold = Some(rust_resettable_hold_fn::<T>); + self.phases.hold = Some(rust_resettable_hold_fn::<T>); } if <T as ResettablePhasesImpl>::EXIT.is_some() { - rc.phases.exit = Some(rust_resettable_exit_fn::<T>); + self.phases.exit = Some(rust_resettable_exit_fn::<T>); } } } -impl<T> ClassInitImpl<DeviceClass> for T -where - T: ClassInitImpl<ObjectClass> + ClassInitImpl<ResettableClass> + DeviceImpl, -{ - fn class_init(dc: &mut DeviceClass) { +impl DeviceClass { + /// Fill in the virtual methods of `DeviceClass` based on the definitions in + /// the `DeviceImpl` trait. + pub fn class_init<T: DeviceImpl>(&mut self) { if <T as DeviceImpl>::REALIZE.is_some() { - dc.realize = Some(rust_realize_fn::<T>); + self.realize = Some(rust_realize_fn::<T>); } if let Some(vmsd) = <T as DeviceImpl>::vmsd() { - dc.vmsd = vmsd; + self.vmsd = vmsd; } let prop = <T as DeviceImpl>::properties(); if !prop.is_empty() { unsafe { - bindings::device_class_set_props_n(dc, prop.as_ptr(), prop.len()); + bindings::device_class_set_props_n(self, prop.as_ptr(), prop.len()); } } - ResettableClass::interface_init::<T, DeviceState>(dc); - <T as ClassInitImpl<ObjectClass>>::class_init(&mut dc.parent_class); + ResettableClass::cast::<DeviceState>(self).class_init::<T>(); + self.parent_class.class_init::<T>(); } } diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 3d5ab2d901..5488643a2f 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -37,11 +37,8 @@ //! * a trait for virtual method implementations, for example `DeviceImpl`. //! Child classes implement this trait to provide their own behavior for //! virtual methods. The trait's methods take `&self` to access instance data. -//! -//! * an implementation of [`ClassInitImpl`], for example -//! `ClassInitImpl<DeviceClass>`. This fills the vtable in the class struct; -//! the source for this is the `*Impl` trait; the associated consts and -//! functions if needed are wrapped to map C types into Rust types. +//! The traits have the appropriate specialization of `IsA<>` as a supertrait, +//! for example `IsA<DeviceState>` for `DeviceImpl`. //! //! * a trait for instance methods, for example `DeviceMethods`. This trait is //! automatically implemented for any reference or smart pointer to a device @@ -52,6 +49,48 @@ //! This provides access to class-wide functionality that doesn't depend on //! instance data. Like instance methods, these are automatically inherited by //! child classes. +//! +//! # Class structures +//! +//! Each QOM class that has virtual methods describes them in a +//! _class struct_. Class structs include a parent field corresponding +//! to the vtable of the parent class, all the way up to [`ObjectClass`]. +//! +//! As mentioned above, virtual methods are defined via traits such as +//! `DeviceImpl`. Class structs do not define any trait but, conventionally, +//! all of them have a `class_init` method to initialize the virtual methods +//! based on the trait and then call the same method on the superclass. +//! +//! ```ignore +//! impl YourSubclassClass +//! { +//! pub fn class_init<T: YourSubclassImpl>(&mut self) { +//! ... +//! klass.parent_class::class_init<T>(); +//! } +//! } +//! ``` +//! +//! If a class implements a QOM interface. In that case, the function must +//! contain, for each interface, an extra forwarding call as follows: +//! +//! ```ignore +//! ResettableClass::cast::<Self>(self).class_init::<Self>(); +//! ``` +//! +//! These `class_init` functions are methods on the class rather than a trait, +//! because the bound on `T` (`DeviceImpl` in this case), will change for every +//! class struct. The functions are pointed to by the +//! [`ObjectImpl::CLASS_INIT`] function pointer. While there is no default +//! implementation, in most cases it will be enough to write it as follows: +//! +//! ```ignore +//! const CLASS_INIT: fn(&mut Self::Class)> = Self::Class::class_init::<Self>; +//! ``` +//! +//! This design incurs a small amount of code duplication but, by not using +//! traits, it allows the flexibility of implementing bindings in any crate, +//! without incurring into violations of orphan rules for traits. use std::{ ffi::CStr, @@ -178,7 +217,7 @@ unsafe extern "C" fn rust_instance_post_init<T: ObjectImpl>(obj: *mut Object) { T::INSTANCE_POST_INIT.unwrap()(unsafe { state.as_ref() }); } -unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( +unsafe extern "C" fn rust_class_init<T: ObjectType + ObjectImpl>( klass: *mut ObjectClass, _data: *mut c_void, ) { @@ -188,7 +227,7 @@ unsafe extern "C" fn rust_class_init<T: ObjectType + ClassInitImpl<T::Class>>( // SAFETY: klass is a T::Class, since rust_class_init<T> // is called from QOM core as the class_init function // for class T - T::class_init(unsafe { klass.as_mut() }) + <T as ObjectImpl>::CLASS_INIT(unsafe { klass.as_mut() }) } unsafe extern "C" fn drop_object<T: ObjectImpl>(obj: *mut Object) { @@ -277,19 +316,25 @@ pub unsafe trait InterfaceType: Sized { /// for this interface. const TYPE_NAME: &'static CStr; - /// Initialize the vtable for the interface; the generic argument `T` is the - /// type being initialized, while the generic argument `U` is the type that + /// Return the vtable for the interface; `U` is the type that /// lists the interface in its `TypeInfo`. /// + /// # Examples + /// + /// This function is usually called by a `class_init` method in `U::Class`. + /// For example, `DeviceClass::class_init<T>` initializes its `Resettable` + /// interface as follows: + /// + /// ```ignore + /// ResettableClass::cast::<DeviceState>(self).class_init::<T>(); + /// ``` + /// + /// where `T` is the concrete subclass that is being initialized. + /// /// # Panics /// /// Panic if the incoming argument if `T` does not implement the interface. - fn interface_init< - T: ObjectType + ClassInitImpl<Self> + ClassInitImpl<U::Class>, - U: ObjectType, - >( - klass: &mut U::Class, - ) { + fn cast<U: ObjectType>(klass: &mut U::Class) -> &mut Self { unsafe { // SAFETY: upcasting to ObjectClass is always valid, and the // return type is either NULL or the argument itself @@ -298,8 +343,7 @@ pub unsafe trait InterfaceType: Sized { Self::TYPE_NAME.as_ptr(), ) .cast(); - - <T as ClassInitImpl<Self>>::class_init(result.as_mut().unwrap()) + result.as_mut().unwrap() } } } @@ -497,7 +541,7 @@ impl<T: ObjectType> ObjectDeref for &mut T {} impl<T: ObjectType> ObjectCastMut for &mut T {} /// Trait a type must implement to be registered with QEMU. -pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> { +pub trait ObjectImpl: ObjectType + IsA<Object> { /// The parent of the type. This should match the first field of the /// struct that implements `ObjectImpl`, minus the `ParentField<_>` wrapper. type ParentType: ObjectType; @@ -550,85 +594,26 @@ pub trait ObjectImpl: ObjectType + ClassInitImpl<Self::Class> { // methods on ObjectClass const UNPARENT: Option<fn(&Self)> = None; -} -/// Internal trait used to automatically fill in a class struct. -/// -/// Each QOM class that has virtual methods describes them in a -/// _class struct_. Class structs include a parent field corresponding -/// to the vtable of the parent class, all the way up to [`ObjectClass`]. -/// Each QOM type has one such class struct; this trait takes care of -/// initializing the `T` part of the class struct, for the type that -/// implements the trait. -/// -/// Each struct will implement this trait with `T` equal to each -/// superclass. For example, a device should implement at least -/// `ClassInitImpl<`[`DeviceClass`](crate::qdev::DeviceClass)`>` and -/// `ClassInitImpl<`[`ObjectClass`]`>`. Such implementations are made -/// in one of two ways. -/// -/// For most superclasses, `ClassInitImpl` is provided by the `qemu-api` -/// crate itself. The Rust implementation of methods will come from a -/// trait like [`ObjectImpl`] or [`DeviceImpl`](crate::qdev::DeviceImpl), -/// and `ClassInitImpl` is provided by blanket implementations that -/// operate on all implementors of the `*Impl`* trait. For example: -/// -/// ```ignore -/// impl<T> ClassInitImpl<DeviceClass> for T -/// where -/// T: ClassInitImpl<ObjectClass> + DeviceImpl, -/// ``` -/// -/// The bound on `ClassInitImpl<ObjectClass>` is needed so that, -/// after initializing the `DeviceClass` part of the class struct, -/// the parent [`ObjectClass`] is initialized as well. -/// -/// The other case is when manual implementation of the trait is needed. -/// This covers the following cases: -/// -/// * if a class implements a QOM interface, the Rust code _has_ to define its -/// own class struct `FooClass` and implement `ClassInitImpl<FooClass>`. -/// `ClassInitImpl<FooClass>`'s `class_init` method will then forward to -/// multiple other `class_init`s, for the interfaces as well as the -/// superclass. (Note that there is no Rust example yet for using interfaces). -/// -/// * for classes implemented outside the ``qemu-api`` crate, it's not possible -/// to add blanket implementations like the above one, due to orphan rules. In -/// that case, the easiest solution is to implement -/// `ClassInitImpl<YourSuperclass>` for each subclass and not have a -/// `YourSuperclassImpl` trait at all. -/// -/// ```ignore -/// impl ClassInitImpl<YourSuperclass> for YourSubclass { -/// fn class_init(klass: &mut YourSuperclass) { -/// klass.some_method = Some(Self::some_method); -/// <Self as ClassInitImpl<SysBusDeviceClass>>::class_init(&mut klass.parent_class); -/// } -/// } -/// ``` -/// -/// While this method incurs a small amount of code duplication, -/// it is generally limited to the recursive call on the last line. -/// This is because classes defined in Rust do not need the same -/// glue code that is needed when the classes are defined in C code. -/// You may consider using a macro if you have many subclasses. -pub trait ClassInitImpl<T> { - /// Initialize `klass` to point to the virtual method implementations + /// Store into the argument the virtual method implementations /// for `Self`. On entry, the virtual method pointers are set to /// the default values coming from the parent classes; the function /// can change them to override virtual methods of a parent class. /// - /// The virtual method implementations usually come from another - /// trait, for example [`DeviceImpl`](crate::qdev::DeviceImpl) - /// when `T` is [`DeviceClass`](crate::qdev::DeviceClass). + /// Usually defined simply as `Self::Class::class_init::<Self>`; + /// however a default implementation cannot be included here, because the + /// bounds that the `Self::Class::class_init` method places on `Self` are + /// not known in advance. /// - /// On entry, `klass`'s parent class is initialized, while the other fields + /// # Safety + /// + /// While `klass`'s parent class is initialized on entry, the other fields /// are all zero; it is therefore assumed that all fields in `T` can be /// zeroed, otherwise it would not be possible to provide the class as a /// `&mut T`. TODO: add a bound of [`Zeroable`](crate::zeroable::Zeroable) /// to T; this is more easily done once Zeroable does not require a manual /// implementation (Rust 1.75.0). - fn class_init(klass: &mut T); + const CLASS_INIT: fn(&mut Self::Class); } /// # Safety @@ -641,13 +626,12 @@ unsafe extern "C" fn rust_unparent_fn<T: ObjectImpl>(dev: *mut Object) { T::UNPARENT.unwrap()(unsafe { state.as_ref() }); } -impl<T> ClassInitImpl<ObjectClass> for T -where - T: ObjectImpl, -{ - fn class_init(oc: &mut ObjectClass) { +impl ObjectClass { + /// Fill in the virtual methods of `ObjectClass` based on the definitions in + /// the `ObjectImpl` trait. + pub fn class_init<T: ObjectImpl>(&mut self) { if <T as ObjectImpl>::UNPARENT.is_some() { - oc.unparent = Some(rust_unparent_fn::<T>); + self.unparent = Some(rust_unparent_fn::<T>); } } } diff --git a/rust/qemu-api/src/sysbus.rs b/rust/qemu-api/src/sysbus.rs index fa36e12178..04821a2b9b 100644 --- a/rust/qemu-api/src/sysbus.rs +++ b/rust/qemu-api/src/sysbus.rs @@ -14,8 +14,8 @@ use crate::{ irq::{IRQState, InterruptSource}, memory::MemoryRegion, prelude::*, - qdev::{DeviceClass, DeviceState}, - qom::{ClassInitImpl, Owned}, + qdev::{DeviceImpl, DeviceState}, + qom::Owned, }; unsafe impl ObjectType for SysBusDevice { @@ -25,13 +25,14 @@ unsafe impl ObjectType for SysBusDevice { } qom_isa!(SysBusDevice: DeviceState, Object); -// TODO: add SysBusDeviceImpl -impl<T> ClassInitImpl<SysBusDeviceClass> for T -where - T: ClassInitImpl<DeviceClass>, -{ - fn class_init(sdc: &mut SysBusDeviceClass) { - <T as ClassInitImpl<DeviceClass>>::class_init(&mut sdc.parent_class); +// TODO: add virtual methods +pub trait SysBusDeviceImpl: DeviceImpl + IsA<SysBusDevice> {} + +impl SysBusDeviceClass { + /// Fill in the virtual methods of `SysBusDeviceClass` based on the + /// definitions in the `SysBusDeviceImpl` trait. + pub fn class_init<T: SysBusDeviceImpl>(self: &mut SysBusDeviceClass) { + self.parent_class.class_init::<T>(); } } diff --git a/rust/qemu-api/tests/tests.rs b/rust/qemu-api/tests/tests.rs index 92dbfb8a0c..e3985782a3 100644 --- a/rust/qemu-api/tests/tests.rs +++ b/rust/qemu-api/tests/tests.rs @@ -8,13 +8,14 @@ use std::{ }; use qemu_api::{ - bindings::*, + bindings::{module_call_init, module_init_type, object_new, object_unref, qdev_prop_bool}, c_str, cell::{self, BqlCell}, declare_properties, define_property, prelude::*, - qdev::{DeviceClass, DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, - qom::{ClassInitImpl, ObjectImpl, ParentField}, + qdev::{DeviceImpl, DeviceState, Property, ResettablePhasesImpl}, + qom::{ObjectImpl, ParentField}, + sysbus::SysBusDevice, vmstate::VMStateDescription, zeroable::Zeroable, }; @@ -40,6 +41,12 @@ pub struct DummyClass { parent_class: <DeviceState as ObjectType>::Class, } +impl DummyClass { + pub fn class_init<T: DeviceImpl>(self: &mut DummyClass) { + self.parent_class.class_init::<T>(); + } +} + declare_properties! { DUMMY_PROPERTIES, define_property!( @@ -59,6 +66,7 @@ unsafe impl ObjectType for DummyState { impl ObjectImpl for DummyState { type ParentType = DeviceState; const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyClass) = DummyClass::class_init::<Self>; } impl ResettablePhasesImpl for DummyState {} @@ -72,14 +80,6 @@ impl DeviceImpl for DummyState { } } -// `impl<T> ClassInitImpl<DummyClass> for T` doesn't work since it violates -// orphan rule. -impl ClassInitImpl<DummyClass> for DummyState { - fn class_init(klass: &mut DummyClass) { - <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); - } -} - #[derive(qemu_api_macros::offsets)] #[repr(C)] #[derive(qemu_api_macros::Object)] @@ -101,20 +101,15 @@ unsafe impl ObjectType for DummyChildState { impl ObjectImpl for DummyChildState { type ParentType = DummyState; const ABSTRACT: bool = false; + const CLASS_INIT: fn(&mut DummyChildClass) = DummyChildClass::class_init::<Self>; } impl ResettablePhasesImpl for DummyChildState {} impl DeviceImpl for DummyChildState {} -impl ClassInitImpl<DummyClass> for DummyChildState { - fn class_init(klass: &mut DummyClass) { - <Self as ClassInitImpl<DeviceClass>>::class_init(&mut klass.parent_class); - } -} - -impl ClassInitImpl<DummyChildClass> for DummyChildState { - fn class_init(klass: &mut DummyChildClass) { - <Self as ClassInitImpl<DummyClass>>::class_init(&mut klass.parent_class); +impl DummyChildClass { + pub fn class_init<T: DeviceImpl>(self: &mut DummyChildClass) { + self.parent_class.class_init::<T>(); } } diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 30677c3ec9..e461c1531e 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -28,7 +28,7 @@ sub_file="${sub_tdir}/submodule.tar" # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" sub_deinit="" diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 06d07e6c22..6ae9d7febe 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1353,6 +1353,70 @@ sub checkfilename { } } +sub checkspdx { + my ($file, $expr) = @_; + + # Imported Linux headers probably have SPDX tags, but if they + # don't we're not requiring contributors to fix this, as these + # files are not expected to be modified locally in QEMU. + # Also don't accidentally detect own checking code. + if ($file =~ m,include/standard-headers, || + $file =~ m,linux-headers, || + $file =~ m,checkpatch.pl,) { + return; + } + + my $origexpr = $expr; + + # Flatten sub-expressions + $expr =~ s/\(|\)/ /g; + $expr =~ s/OR|AND/ /g; + + # Merge WITH exceptions to the license + $expr =~ s/\s+WITH\s+/-WITH-/g; + + # Cull more leading/trailing whitespace + $expr =~ s/^\s*//g; + $expr =~ s/\s*$//g; + + my @bits = split / +/, $expr; + + my $prefer = "GPL-2.0-or-later"; + my @valid = qw( + GPL-2.0-only + LGPL-2.1-only + LGPL-2.1-or-later + BSD-2-Clause + BSD-3-Clause + MIT + ); + + my $nonpreferred = 0; + my @unknown = (); + foreach my $bit (@bits) { + if ($bit eq $prefer) { + next; + } + if (grep /^$bit$/, @valid) { + $nonpreferred = 1; + } else { + push @unknown, $bit; + } + } + if (@unknown) { + ERROR("Saw unacceptable licenses '" . join(',', @unknown) . + "', valid choices for QEMU are:\n" . join("\n", $prefer, @valid)); + } + + if ($nonpreferred) { + WARN("Saw acceptable license '$origexpr' but note '$prefer' is " . + "preferred for new files unless the code is derived from a " . + "source file with an existing declared license that must be " . + "retained. Please explain the license choice in the commit " . + "message."); + } +} + sub process { my $filename = shift; @@ -1378,6 +1442,8 @@ sub process { my $in_imported_file = 0; my $in_no_imported_file = 0; my $non_utf8_charset = 0; + my $expect_spdx = 0; + my $expect_spdx_file; our @report = (); our $cnt_lines = 0; @@ -1615,6 +1681,51 @@ sub process { WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } +# All new files should have a SPDX-License-Identifier tag + if ($line =~ /^new file mode\s*\d+\s*$/) { + if ($expect_spdx) { + if ($expect_spdx_file =~ + /\.(c|h|py|pl|sh|json|inc|Makefile)$/) { + # source code files MUST have SPDX license declared + ERROR("New file '$expect_spdx_file' requires " . + "'SPDX-License-Identifer'"); + } else { + # Other files MAY have SPDX license if appropriate + WARN("Does new file '$expect_spdx_file' need " . + "'SPDX-License-Identifer'?"); + } + } + $expect_spdx = 1; + $expect_spdx_file = undef; + } elsif ($expect_spdx) { + $expect_spdx_file = $realfile unless + defined $expect_spdx_file; + + # SPDX tags may occurr in comments which were + # stripped from '$line', so use '$rawline' + if ($rawline =~ /SPDX-License-Identifier/) { + $expect_spdx = 0; + $expect_spdx_file = undef; + } + } + +# Check SPDX-License-Identifier references a permitted license + if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { + &checkspdx($realfile, $1); + } + + if ($rawline =~ m,(SPDX-[a-zA-Z0-9-_]+):,) { + my $tag = $1; + my @permitted = qw( + SPDX-License-Identifier + ); + + unless (grep { /^$tag$/ } @permitted) { + ERROR("Tag $tag not permitted in QEMU code, valid " . + "choices are: " . join(", ", @permitted)); + } + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("patch seems to be corrupt (line wrapped?)\n" . diff --git a/scripts/make-release b/scripts/make-release index 1b89b3423a..8c3594a1a4 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -41,7 +41,7 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index a8066aab03..3e8e00852b 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -168,6 +168,7 @@ meson_options_help() { printf "%s\n" ' pixman pixman support' printf "%s\n" ' plugins TCG plugins via shared library loading' printf "%s\n" ' png PNG support with libpng' + printf "%s\n" ' pvg macOS paravirtualized graphics support' printf "%s\n" ' qatzip QATzip compression support' printf "%s\n" ' qcow1 qcow1 image format support' printf "%s\n" ' qed qed image format support' @@ -436,6 +437,8 @@ _meson_option_parse() { --enable-png) printf "%s" -Dpng=enabled ;; --disable-png) printf "%s" -Dpng=disabled ;; --prefix=*) quote_sh "-Dprefix=$2" ;; + --enable-pvg) printf "%s" -Dpvg=enabled ;; + --disable-pvg) printf "%s" -Dpvg=disabled ;; --enable-qatzip) printf "%s" -Dqatzip=enabled ;; --disable-qatzip) printf "%s" -Dqatzip=disabled ;; --enable-qcow1) printf "%s" -Dqcow1=enabled ;; diff --git a/stubs/meson.build b/stubs/meson.build index b0fee37e05..63392f5e78 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -62,7 +62,7 @@ if have_user stub_ss.add(files('qdev.c')) endif - stub_ss.add(files('monitor-fd.c')) + stub_ss.add(files('monitor-internal.c')) endif if have_system diff --git a/stubs/monitor-fd.c b/stubs/monitor-fd.c deleted file mode 100644 index 9bb6749885..0000000000 --- a/stubs/monitor-fd.c +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "qemu/osdep.h" -#include "monitor/monitor.h" - -int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) -{ - abort(); -} diff --git a/subprojects/.gitignore b/subprojects/.gitignore index 50f173f90d..d12d34618c 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -11,6 +11,7 @@ /bilge-impl-0.2.0 /either-1.12.0 /itertools-0.11.0 +/libc-0.2.162 /proc-macro-error-1.0.4 /proc-macro-error-attr-1.0.4 /proc-macro2-1.0.84 diff --git a/subprojects/libc-0.2-rs.wrap b/subprojects/libc-0.2-rs.wrap new file mode 100644 index 0000000000..bbe08f8788 --- /dev/null +++ b/subprojects/libc-0.2-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = libc-0.2.162 +source_url = https://crates.io/api/v1/crates/libc/0.2.162/download +source_filename = libc-0.2.162.tar.gz +source_hash = 18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398 +#method = cargo +patch_directory = libc-0.2-rs diff --git a/subprojects/packagefiles/libc-0.2-rs/meson.build b/subprojects/packagefiles/libc-0.2-rs/meson.build new file mode 100644 index 0000000000..ac4f80dba9 --- /dev/null +++ b/subprojects/packagefiles/libc-0.2-rs/meson.build @@ -0,0 +1,37 @@ +project('libc-0.2-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.162', + license: 'MIT OR Apache-2.0', + default_options: []) + +_libc_rs = static_library( + 'libc', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2015', 'build.rust_std=2015'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + '--cfg', 'freebsd11', + '--cfg', 'libc_priv_mod_use', + '--cfg', 'libc_union', + '--cfg', 'libc_const_size_of', + '--cfg', 'libc_align', + '--cfg', 'libc_int128', + '--cfg', 'libc_core_cvoid', + '--cfg', 'libc_packedN', + '--cfg', 'libc_cfg_target_vendor', + '--cfg', 'libc_non_exhaustive', + '--cfg', 'libc_long_array', + '--cfg', 'libc_ptr_addr_of', + '--cfg', 'libc_underscore_const_names', + '--cfg', 'libc_const_extern_fn', + ], + dependencies: [], +) + +libc_dep = declare_dependency( + link_with: _libc_rs, +) + +meson.override_dependency('libc-0.2-rs', libc_dep) diff --git a/system/device_tree-stub.c b/system/device_tree-stub.c index bddda6fa37..428330b0fe 100644 --- a/system/device_tree-stub.c +++ b/system/device_tree-stub.c @@ -5,6 +5,9 @@ #ifdef CONFIG_FDT void qmp_dumpdtb(const char *filename, Error **errp) { - error_setg(errp, "This machine doesn't have a FDT"); + ERRP_GUARD(); + + error_setg(errp, "This machine doesn't have an FDT"); + error_append_hint(errp, "(this machine type definitely doesn't use FDT)\n"); } #endif diff --git a/system/device_tree.c b/system/device_tree.c index 4bc2d61b93..aa3fe9516f 100644 --- a/system/device_tree.c +++ b/system/device_tree.c @@ -594,21 +594,6 @@ int qemu_fdt_add_path(void *fdt, const char *path) return retval; } -void qemu_fdt_dumpdtb(void *fdt, int size) -{ - const char *dumpdtb = current_machine->dumpdtb; - - if (dumpdtb) { - /* Dump the dtb to a file and quit */ - if (g_file_set_contents(dumpdtb, fdt, size, NULL)) { - info_report("dtb dumped to %s. Exiting.", dumpdtb); - exit(0); - } - error_report("%s: Failed dumping dtb to %s", __func__, dumpdtb); - exit(1); - } -} - int qemu_fdt_setprop_sized_cells_from_array(void *fdt, const char *node_path, const char *property, @@ -650,11 +635,16 @@ out: void qmp_dumpdtb(const char *filename, Error **errp) { + ERRP_GUARD(); + g_autoptr(GError) err = NULL; uint32_t size; if (!current_machine->fdt) { - error_setg(errp, "This machine doesn't have a FDT"); + error_setg(errp, "This machine doesn't have an FDT"); + error_append_hint(errp, + "(Perhaps it doesn't support FDT at all, or perhaps " + "you need to provide an FDT with the -fdt option?)\n"); return; } diff --git a/system/physmem.c b/system/physmem.c index 67bdf631e6..eff8b55c2d 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1882,7 +1882,11 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) if (new_block->flags & RAM_GUEST_MEMFD) { int ret; - assert(kvm_enabled()); + if (!kvm_enabled()) { + error_setg(errp, "cannot set up private guest memory for %s: KVM required", + object_get_typename(OBJECT(current_machine->cgs))); + goto out_free; + } assert(new_block->guest_memfd < 0); ret = ram_block_discard_require(true); diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 0afd96018e..2439af63a0 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -899,6 +899,18 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) clamp_id_aa64mmfr0_parange_to_ipa_size(&host_isar.id_aa64mmfr0); + /* + * Disable SME, which is not properly handled by QEMU hvf yet. + * To allow this through we would need to: + * - make sure that the SME state is correctly handled in the + * get_registers/put_registers functions + * - get the SME-specific CPU properties to work with accelerators + * other than TCG + * - fix any assumptions we made that SME implies SVE (since + * on the M4 there is SME but not SVE) + */ + host_isar.id_aa64pfr1 &= ~R_ID_AA64PFR1_SME_MASK; + ahcf->isar = host_isar; /* @@ -1971,6 +1983,7 @@ int hvf_vcpu_exec(CPUState *cpu) bool isv = syndrome & ARM_EL_ISV; bool iswrite = (syndrome >> 6) & 1; bool s1ptw = (syndrome >> 7) & 1; + bool sse = (syndrome >> 21) & 1; uint32_t sas = (syndrome >> 22) & 3; uint32_t len = 1 << sas; uint32_t srt = (syndrome >> 16) & 0x1f; @@ -1998,6 +2011,9 @@ int hvf_vcpu_exec(CPUState *cpu) address_space_read(&address_space_memory, hvf_exit->exception.physical_address, MEMTXATTRS_UNSPECIFIED, &val, len); + if (sse) { + val = sextract64(val, 0, len * 8); + } hvf_set_reg(cpu, srt, val); } diff --git a/target/arm/internals.h b/target/arm/internals.h index b318734145..a6ff228f9f 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1833,5 +1833,14 @@ int alle1_tlbmask(CPUARMState *env); void arm_set_default_fp_behaviours(float_status *s); /* Set the float_status behaviour to match Arm FPCR.AH=1 behaviour */ void arm_set_ah_fp_behaviours(float_status *s); +/* Read the float_status info and return the appropriate FPSR value */ +uint32_t vfp_get_fpsr_from_host(CPUARMState *env); +/* Clear the exception status flags from all float_status fields */ +void vfp_clear_float_status_exc_flags(CPUARMState *env); +/* + * Update float_status fields to handle the bits of the FPCR + * specified by mask changing to the values in val. + */ +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask); #endif diff --git a/target/arm/meson.build b/target/arm/meson.build index 2e10464dbb..3065081d24 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -4,7 +4,7 @@ arm_ss.add(files( 'debug_helper.c', 'gdbstub.c', 'helper.c', - 'vfp_helper.c', + 'vfp_fpscr.c', )) arm_ss.add(zlib) diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index f3f45d54f2..93a15cad61 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -30,3 +30,25 @@ void assert_hflags_rebuild_correctly(CPUARMState *env) void define_tlb_insn_regs(ARMCPU *cpu) { } + +/* With KVM, we never use float_status, so these can be no-ops */ +void arm_set_default_fp_behaviours(float_status *s) +{ +} + +void arm_set_ah_fp_behaviours(float_status *s) +{ +} + +uint32_t vfp_get_fpsr_from_host(CPUARMState *env) +{ + return 0; +} + +void vfp_clear_float_status_exc_flags(CPUARMState *env) +{ +} + +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) +{ +} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 1f9077c372..dd12ccedb1 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -41,6 +41,7 @@ arm_ss.add(files( 'vec_helper.c', 'tlb-insns.c', 'arith_helper.c', + 'vfp_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( diff --git a/target/arm/vfp_helper.c b/target/arm/tcg/vfp_helper.c index 5d424477a2..b32e2f4e27 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/tcg/vfp_helper.c @@ -23,13 +23,7 @@ #include "internals.h" #include "cpu-features.h" #include "fpu/softfloat.h" -#ifdef CONFIG_TCG #include "qemu/log.h" -#endif - -/* VFP support. We follow the convention used for VFP instructions: - Single precision routines have a "s" suffix, double precision a - "d" suffix. */ /* * Set the float_status behaviour to match the Arm defaults: @@ -75,8 +69,6 @@ void arm_set_ah_fp_behaviours(float_status *s) set_float_default_nan_pattern(0b11000000, s); } -#ifdef CONFIG_TCG - /* Convert host exception flags to vfp form. */ static inline uint32_t vfp_exceptbits_from_host(int host_bits, bool ah) { @@ -113,7 +105,7 @@ static inline uint32_t vfp_exceptbits_from_host(int host_bits, bool ah) return target_bits; } -static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) +uint32_t vfp_get_fpsr_from_host(CPUARMState *env) { uint32_t a32_flags = 0, a64_flags = 0; @@ -148,7 +140,7 @@ static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) vfp_exceptbits_from_host(a32_flags, false); } -static void vfp_clear_float_status_exc_flags(CPUARMState *env) +void vfp_clear_float_status_exc_flags(CPUARMState *env) { /* * Clear out all the exception-flag information in the float_status @@ -176,7 +168,7 @@ static void vfp_sync_and_clear_float_status_exc_flags(CPUARMState *env) vfp_clear_float_status_exc_flags(env); } -static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) +void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) { uint64_t changed = env->vfp.fpcr; @@ -261,166 +253,11 @@ static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) } } -#else - -static uint32_t vfp_get_fpsr_from_host(CPUARMState *env) -{ - return 0; -} - -static void vfp_clear_float_status_exc_flags(CPUARMState *env) -{ -} - -static void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) -{ -} - -#endif - -uint32_t vfp_get_fpcr(CPUARMState *env) -{ - uint32_t fpcr = env->vfp.fpcr - | (env->vfp.vec_len << 16) - | (env->vfp.vec_stride << 20); - - /* - * M-profile LTPSIZE is the same bits [18:16] as A-profile Len; whichever - * of the two is not applicable to this CPU will always be zero. - */ - fpcr |= env->v7m.ltpsize << 16; - - return fpcr; -} - -uint32_t vfp_get_fpsr(CPUARMState *env) -{ - uint32_t fpsr = env->vfp.fpsr; - uint32_t i; - - fpsr |= vfp_get_fpsr_from_host(env); - - i = env->vfp.qc[0] | env->vfp.qc[1] | env->vfp.qc[2] | env->vfp.qc[3]; - fpsr |= i ? FPSR_QC : 0; - return fpsr; -} - -uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) -{ - return (vfp_get_fpcr(env) & FPSCR_FPCR_MASK) | - (vfp_get_fpsr(env) & FPSCR_FPSR_MASK); -} - -uint32_t vfp_get_fpscr(CPUARMState *env) -{ - return HELPER(vfp_get_fpscr)(env); -} - -void vfp_set_fpsr(CPUARMState *env, uint32_t val) -{ - ARMCPU *cpu = env_archcpu(env); - - if (arm_feature(env, ARM_FEATURE_NEON) || - cpu_isar_feature(aa32_mve, cpu)) { - /* - * The bit we set within vfp.qc[] is arbitrary; the array as a - * whole being zero/non-zero is what counts. - */ - env->vfp.qc[0] = val & FPSR_QC; - env->vfp.qc[1] = 0; - env->vfp.qc[2] = 0; - env->vfp.qc[3] = 0; - } - - /* - * NZCV lives only in env->vfp.fpsr. The cumulative exception flags - * IOC|DZC|OFC|UFC|IXC|IDC also live in env->vfp.fpsr, with possible - * extra pending exception information that hasn't yet been folded in - * living in the float_status values (for TCG). - * Since this FPSR write gives us the up to date values of the exception - * flags, we want to store into vfp.fpsr the NZCV and CEXC bits, zeroing - * anything else. We also need to clear out the float_status exception - * information so that the next vfp_get_fpsr does not fold in stale data. - */ - val &= FPSR_NZCV_MASK | FPSR_CEXC_MASK; - env->vfp.fpsr = val; - vfp_clear_float_status_exc_flags(env); -} - -static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) -{ - /* - * We only set FPCR bits defined by mask, and leave the others alone. - * We assume the mask is sensible (e.g. doesn't try to set only - * part of a field) - */ - ARMCPU *cpu = env_archcpu(env); - - /* When ARMv8.2-FP16 is not supported, FZ16 is RES0. */ - if (!cpu_isar_feature(any_fp16, cpu)) { - val &= ~FPCR_FZ16; - } - if (!cpu_isar_feature(aa64_afp, cpu)) { - val &= ~(FPCR_FIZ | FPCR_AH | FPCR_NEP); - } - - if (!cpu_isar_feature(aa64_ebf16, cpu)) { - val &= ~FPCR_EBF; - } - - vfp_set_fpcr_to_host(env, val, mask); - - if (mask & (FPCR_LEN_MASK | FPCR_STRIDE_MASK)) { - if (!arm_feature(env, ARM_FEATURE_M)) { - /* - * Short-vector length and stride; on M-profile these bits - * are used for different purposes. - * We can't make this conditional be "if MVFR0.FPShVec != 0", - * because in v7A no-short-vector-support cores still had to - * allow Stride/Len to be written with the only effect that - * some insns are required to UNDEF if the guest sets them. - */ - env->vfp.vec_len = extract32(val, 16, 3); - env->vfp.vec_stride = extract32(val, 20, 2); - } else if (cpu_isar_feature(aa32_mve, cpu)) { - env->v7m.ltpsize = extract32(val, FPCR_LTPSIZE_SHIFT, - FPCR_LTPSIZE_LENGTH); - } - } - - /* - * We don't implement trapped exception handling, so the - * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) - * - * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF, FZ16, - * FIZ, AH, and NEP. - * Len, Stride and LTPSIZE we just handled. Store those bits - * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI - * bits. - */ - val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 | - FPCR_EBF | FPCR_FIZ | FPCR_AH | FPCR_NEP; - env->vfp.fpcr &= ~mask; - env->vfp.fpcr |= val; -} - -void vfp_set_fpcr(CPUARMState *env, uint32_t val) -{ - vfp_set_fpcr_masked(env, val, MAKE_64BIT_MASK(0, 32)); -} - -void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) -{ - vfp_set_fpcr_masked(env, val, FPSCR_FPCR_MASK); - vfp_set_fpsr(env, val & FPSCR_FPSR_MASK); -} - -void vfp_set_fpscr(CPUARMState *env, uint32_t val) -{ - HELPER(vfp_set_fpscr)(env, val); -} - -#ifdef CONFIG_TCG +/* + * VFP support. We follow the convention used for VFP instructions: + * Single precision routines have a "s" suffix, double precision a + * "d" suffix. + */ #define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) @@ -1520,4 +1357,12 @@ void HELPER(check_hcr_el2_trap)(CPUARMState *env, uint32_t rt, uint32_t reg) raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); } -#endif +uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +{ + return vfp_get_fpscr(env); +} + +void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +{ + vfp_set_fpscr(env, val); +} diff --git a/target/arm/vfp_fpscr.c b/target/arm/vfp_fpscr.c new file mode 100644 index 0000000000..92ea60ebbf --- /dev/null +++ b/target/arm/vfp_fpscr.c @@ -0,0 +1,155 @@ +/* + * ARM VFP floating-point: handling of FPSCR/FPCR/FPSR + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" +#include "cpu-features.h" + +uint32_t vfp_get_fpcr(CPUARMState *env) +{ + uint32_t fpcr = env->vfp.fpcr + | (env->vfp.vec_len << 16) + | (env->vfp.vec_stride << 20); + + /* + * M-profile LTPSIZE is the same bits [18:16] as A-profile Len; whichever + * of the two is not applicable to this CPU will always be zero. + */ + fpcr |= env->v7m.ltpsize << 16; + + return fpcr; +} + +uint32_t vfp_get_fpsr(CPUARMState *env) +{ + uint32_t fpsr = env->vfp.fpsr; + uint32_t i; + + fpsr |= vfp_get_fpsr_from_host(env); + + i = env->vfp.qc[0] | env->vfp.qc[1] | env->vfp.qc[2] | env->vfp.qc[3]; + fpsr |= i ? FPSR_QC : 0; + return fpsr; +} + +uint32_t vfp_get_fpscr(CPUARMState *env) +{ + return (vfp_get_fpcr(env) & FPSCR_FPCR_MASK) | + (vfp_get_fpsr(env) & FPSCR_FPSR_MASK); +} + +void vfp_set_fpsr(CPUARMState *env, uint32_t val) +{ + ARMCPU *cpu = env_archcpu(env); + + if (arm_feature(env, ARM_FEATURE_NEON) || + cpu_isar_feature(aa32_mve, cpu)) { + /* + * The bit we set within vfp.qc[] is arbitrary; the array as a + * whole being zero/non-zero is what counts. + */ + env->vfp.qc[0] = val & FPSR_QC; + env->vfp.qc[1] = 0; + env->vfp.qc[2] = 0; + env->vfp.qc[3] = 0; + } + + /* + * NZCV lives only in env->vfp.fpsr. The cumulative exception flags + * IOC|DZC|OFC|UFC|IXC|IDC also live in env->vfp.fpsr, with possible + * extra pending exception information that hasn't yet been folded in + * living in the float_status values (for TCG). + * Since this FPSR write gives us the up to date values of the exception + * flags, we want to store into vfp.fpsr the NZCV and CEXC bits, zeroing + * anything else. We also need to clear out the float_status exception + * information so that the next vfp_get_fpsr does not fold in stale data. + */ + val &= FPSR_NZCV_MASK | FPSR_CEXC_MASK; + env->vfp.fpsr = val; + vfp_clear_float_status_exc_flags(env); +} + +static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) +{ + /* + * We only set FPCR bits defined by mask, and leave the others alone. + * We assume the mask is sensible (e.g. doesn't try to set only + * part of a field) + */ + ARMCPU *cpu = env_archcpu(env); + + /* When ARMv8.2-FP16 is not supported, FZ16 is RES0. */ + if (!cpu_isar_feature(any_fp16, cpu)) { + val &= ~FPCR_FZ16; + } + if (!cpu_isar_feature(aa64_afp, cpu)) { + val &= ~(FPCR_FIZ | FPCR_AH | FPCR_NEP); + } + + if (!cpu_isar_feature(aa64_ebf16, cpu)) { + val &= ~FPCR_EBF; + } + + vfp_set_fpcr_to_host(env, val, mask); + + if (mask & (FPCR_LEN_MASK | FPCR_STRIDE_MASK)) { + if (!arm_feature(env, ARM_FEATURE_M)) { + /* + * Short-vector length and stride; on M-profile these bits + * are used for different purposes. + * We can't make this conditional be "if MVFR0.FPShVec != 0", + * because in v7A no-short-vector-support cores still had to + * allow Stride/Len to be written with the only effect that + * some insns are required to UNDEF if the guest sets them. + */ + env->vfp.vec_len = extract32(val, 16, 3); + env->vfp.vec_stride = extract32(val, 20, 2); + } else if (cpu_isar_feature(aa32_mve, cpu)) { + env->v7m.ltpsize = extract32(val, FPCR_LTPSIZE_SHIFT, + FPCR_LTPSIZE_LENGTH); + } + } + + /* + * We don't implement trapped exception handling, so the + * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) + * + * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF, FZ16, + * FIZ, AH, and NEP. + * Len, Stride and LTPSIZE we just handled. Store those bits + * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI + * bits. + */ + val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 | + FPCR_EBF | FPCR_FIZ | FPCR_AH | FPCR_NEP; + env->vfp.fpcr &= ~mask; + env->vfp.fpcr |= val; +} + +void vfp_set_fpcr(CPUARMState *env, uint32_t val) +{ + vfp_set_fpcr_masked(env, val, MAKE_64BIT_MASK(0, 32)); +} + +void vfp_set_fpscr(CPUARMState *env, uint32_t val) +{ + vfp_set_fpcr_masked(env, val, FPSCR_FPCR_MASK); + vfp_set_fpsr(env, val & FPSCR_FPSR_MASK); +} diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index 8ff4b44804..a62d9d3083 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -67,6 +67,7 @@ void HELPER(loaded_fr0)(CPUHPPAState *env) set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status); /* Default NaN: sign bit clear, msb-1 frac bit set */ set_float_default_nan_pattern(0b00100000, &env->fp_status); + set_snan_bit_is_one(true, &env->fp_status); /* * "PA-RISC 2.0 Architecture" says it is IMPDEF whether the flushing * enabled by FPSR.D happens before or after rounding. We pick "before" diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 72ab147e85..0cd9b70938 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -247,6 +247,9 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, case CPU_TOPOLOGY_LEVEL_CORE: num_ids = 1 << apicid_core_offset(topo_info); break; + case CPU_TOPOLOGY_LEVEL_MODULE: + num_ids = 1 << apicid_module_offset(topo_info); + break; case CPU_TOPOLOGY_LEVEL_DIE: num_ids = 1 << apicid_die_offset(topo_info); break; @@ -255,7 +258,7 @@ static uint32_t max_thread_ids_for_cache(X86CPUTopoInfo *topo_info, break; default: /* - * Currently there is no use case for THREAD and MODULE, so use + * Currently there is no use case for THREAD, so use * assert directly to facilitate debugging. */ g_assert_not_reached(); @@ -5495,6 +5498,130 @@ static const X86CPUDefinition builtin_x86_defs[] = { .model_id = "AMD EPYC-Genoa Processor", .cache_info = &epyc_genoa_cache_info, }, + { + .name = "YongFeng", + .level = 0x1F, + .vendor = CPUID_VENDOR_ZHAOXIN1, + .family = 7, + .model = 11, + .stepping = 3, + /* missing: CPUID_HT, CPUID_TM, CPUID_PBE */ + .features[FEAT_1_EDX] = + CPUID_SS | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_ACPI | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | + CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | + CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | + CPUID_PSE | CPUID_DE | CPUID_VME | CPUID_FP87, + /* + * missing: CPUID_EXT_OSXSAVE, CPUID_EXT_XTPR, CPUID_EXT_TM2, + * CPUID_EXT_EST, CPUID_EXT_SMX, CPUID_EXT_VMX + */ + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_TSC_DEADLINE_TIMER | + CPUID_EXT_POPCNT | CPUID_EXT_MOVBE | CPUID_EXT_X2APIC | + CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | CPUID_EXT_PCID | + CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | + CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_BMI2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_FSGSBASE, + /* missing: CPUID_7_0_ECX_OSPKE */ + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_UMIP, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_SPEC_CTRL, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, + .features[FEAT_8000_0007_EDX] = CPUID_APM_INVTSC, + /* + * TODO: When the Linux kernel introduces other existing definitions + * for this leaf, remember to update the definitions here. + */ + .features[FEAT_C000_0001_EDX] = + CPUID_C000_0001_EDX_PMM_EN | CPUID_C000_0001_EDX_PMM | + CPUID_C000_0001_EDX_PHE_EN | CPUID_C000_0001_EDX_PHE | + CPUID_C000_0001_EDX_ACE2 | + CPUID_C000_0001_EDX_XCRYPT_EN | CPUID_C000_0001_EDX_XCRYPT | + CPUID_C000_0001_EDX_XSTORE_EN | CPUID_C000_0001_EDX_XSTORE, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .features[FEAT_ARCH_CAPABILITIES] = + MSR_ARCH_CAP_RDCL_NO | MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY | + MSR_ARCH_CAP_MDS_NO | MSR_ARCH_CAP_PSCHANGE_MC_NO | + MSR_ARCH_CAP_SSB_NO, + .features[FEAT_VMX_PROCBASED_CTLS] = + VMX_CPU_BASED_VIRTUAL_INTR_PENDING | VMX_CPU_BASED_HLT_EXITING | + VMX_CPU_BASED_USE_TSC_OFFSETING | VMX_CPU_BASED_INVLPG_EXITING | + VMX_CPU_BASED_MWAIT_EXITING | VMX_CPU_BASED_RDPMC_EXITING | + VMX_CPU_BASED_RDTSC_EXITING | VMX_CPU_BASED_CR3_LOAD_EXITING | + VMX_CPU_BASED_CR3_STORE_EXITING | VMX_CPU_BASED_CR8_LOAD_EXITING | + VMX_CPU_BASED_CR8_STORE_EXITING | VMX_CPU_BASED_TPR_SHADOW | + VMX_CPU_BASED_VIRTUAL_NMI_PENDING | VMX_CPU_BASED_MOV_DR_EXITING | + VMX_CPU_BASED_UNCOND_IO_EXITING | VMX_CPU_BASED_USE_IO_BITMAPS | + VMX_CPU_BASED_MONITOR_TRAP_FLAG | VMX_CPU_BASED_USE_MSR_BITMAPS | + VMX_CPU_BASED_MONITOR_EXITING | VMX_CPU_BASED_PAUSE_EXITING | + VMX_CPU_BASED_ACTIVATE_SECONDARY_CONTROLS, + /* + * missing: VMX_SECONDARY_EXEC_PAUSE_LOOP_EXITING, + * VMX_SECONDARY_EXEC_TSC_SCALING + */ + .features[FEAT_VMX_SECONDARY_CTLS] = + VMX_SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | + VMX_SECONDARY_EXEC_ENABLE_EPT | VMX_SECONDARY_EXEC_DESC | + VMX_SECONDARY_EXEC_RDTSCP | VMX_SECONDARY_EXEC_ENABLE_VPID | + VMX_SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | + VMX_SECONDARY_EXEC_WBINVD_EXITING | + VMX_SECONDARY_EXEC_UNRESTRICTED_GUEST | + VMX_SECONDARY_EXEC_APIC_REGISTER_VIRT | + VMX_SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | + VMX_SECONDARY_EXEC_RDRAND_EXITING | + VMX_SECONDARY_EXEC_ENABLE_INVPCID | + VMX_SECONDARY_EXEC_ENABLE_VMFUNC | + VMX_SECONDARY_EXEC_SHADOW_VMCS | + VMX_SECONDARY_EXEC_ENABLE_PML, + .features[FEAT_VMX_PINBASED_CTLS] = + VMX_PIN_BASED_EXT_INTR_MASK | VMX_PIN_BASED_NMI_EXITING | + VMX_PIN_BASED_VIRTUAL_NMIS | VMX_PIN_BASED_VMX_PREEMPTION_TIMER | + VMX_PIN_BASED_POSTED_INTR, + .features[FEAT_VMX_EXIT_CTLS] = + VMX_VM_EXIT_SAVE_DEBUG_CONTROLS | VMX_VM_EXIT_HOST_ADDR_SPACE_SIZE | + VMX_VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_EXIT_ACK_INTR_ON_EXIT | VMX_VM_EXIT_SAVE_IA32_PAT | + VMX_VM_EXIT_LOAD_IA32_PAT | VMX_VM_EXIT_SAVE_IA32_EFER | + VMX_VM_EXIT_LOAD_IA32_EFER | VMX_VM_EXIT_SAVE_VMX_PREEMPTION_TIMER, + /* missing: VMX_VM_ENTRY_SMM, VMX_VM_ENTRY_DEACT_DUAL_MONITOR */ + .features[FEAT_VMX_ENTRY_CTLS] = + VMX_VM_ENTRY_LOAD_DEBUG_CONTROLS | VMX_VM_ENTRY_IA32E_MODE | + VMX_VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL | + VMX_VM_ENTRY_LOAD_IA32_PAT | VMX_VM_ENTRY_LOAD_IA32_EFER, + /* + * missing: MSR_VMX_MISC_ACTIVITY_SHUTDOWN, + * MSR_VMX_MISC_ACTIVITY_WAIT_SIPI + */ + .features[FEAT_VMX_MISC] = + MSR_VMX_MISC_STORE_LMA | MSR_VMX_MISC_ACTIVITY_HLT | + MSR_VMX_MISC_VMWRITE_VMEXIT, + /* missing: MSR_VMX_EPT_UC */ + .features[FEAT_VMX_EPT_VPID_CAPS] = + MSR_VMX_EPT_EXECONLY | MSR_VMX_EPT_PAGE_WALK_LENGTH_4 | + MSR_VMX_EPT_WB | MSR_VMX_EPT_2MB | MSR_VMX_EPT_1GB | + MSR_VMX_EPT_INVEPT | MSR_VMX_EPT_AD_BITS | + MSR_VMX_EPT_INVEPT_SINGLE_CONTEXT | MSR_VMX_EPT_INVEPT_ALL_CONTEXT | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT | MSR_VMX_EPT_INVVPID | + MSR_VMX_EPT_INVVPID_ALL_CONTEXT | MSR_VMX_EPT_INVVPID_SINGLE_ADDR | + MSR_VMX_EPT_INVVPID_SINGLE_CONTEXT_NOGLOBALS, + .features[FEAT_VMX_BASIC] = + MSR_VMX_BASIC_INS_OUTS | MSR_VMX_BASIC_TRUE_CTLS, + .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, + .xlevel = 0x80000008, + .model_id = "Zhaoxin YongFeng Processor", + }, }; /* @@ -7677,9 +7804,10 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) /* * The Linux kernel checks for the CMPLegacy bit and * discards multiple thread information if it is set. - * So don't set it here for Intel to make Linux guests happy. + * So don't set it here for Intel (and other processors + * following Intel's behavior) to make Linux guests happy. */ - if (!IS_INTEL_CPU(env)) { + if (!IS_INTEL_CPU(env) && !IS_ZHAOXIN_CPU(env)) { env->features[FEAT_8000_0001_ECX] |= CPUID_EXT3_CMP_LEG; } } @@ -7903,6 +8031,64 @@ static void x86_cpu_hyperv_realize(X86CPU *cpu) cpu->hyperv_limits[2] = 0; } +#ifndef CONFIG_USER_ONLY +static bool x86_cpu_update_smp_cache_topo(MachineState *ms, X86CPU *cpu, + Error **errp) +{ + CPUX86State *env = &cpu->env; + CpuTopologyLevel level; + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1d_cache->share_level = level; + env->cache_info_amd.l1d_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_cpuid4.l1d_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, + env->cache_info_amd.l1d_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l1i_cache->share_level = level; + env->cache_info_amd.l1i_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_cpuid4.l1i_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, + env->cache_info_amd.l1i_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l2_cache->share_level = level; + env->cache_info_amd.l2_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_cpuid4.l2_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, + env->cache_info_amd.l2_cache->share_level); + } + + level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3); + if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { + env->cache_info_cpuid4.l3_cache->share_level = level; + env->cache_info_amd.l3_cache->share_level = level; + } else { + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_cpuid4.l3_cache->share_level); + machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, + env->cache_info_amd.l3_cache->share_level); + } + + if (!machine_check_smp_cache(ms, errp)) { + return false; + } + return true; +} +#endif + static void x86_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -8142,6 +8328,14 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) #ifndef CONFIG_USER_ONLY MachineState *ms = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (mc->smp_props.has_caches) { + if (!x86_cpu_update_smp_cache_topo(ms, cpu, errp)) { + return; + } + } + qemu_register_reset(x86_cpu_machine_reset_cb, cpu); if (cpu->env.features[FEAT_1_EDX] & CPUID_APIC || ms->smp.cpus > 1) { diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c67b42d34f..10ce019e3f 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1110,6 +1110,27 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* CPUID[0x80000007].EDX flags: */ #define CPUID_APM_INVTSC (1U << 8) +/* "rng" RNG present (xstore) */ +#define CPUID_C000_0001_EDX_XSTORE (1U << 2) +/* "rng_en" RNG enabled */ +#define CPUID_C000_0001_EDX_XSTORE_EN (1U << 3) +/* "ace" on-CPU crypto (xcrypt) */ +#define CPUID_C000_0001_EDX_XCRYPT (1U << 6) +/* "ace_en" on-CPU crypto enabled */ +#define CPUID_C000_0001_EDX_XCRYPT_EN (1U << 7) +/* Advanced Cryptography Engine v2 */ +#define CPUID_C000_0001_EDX_ACE2 (1U << 8) +/* ACE v2 enabled */ +#define CPUID_C000_0001_EDX_ACE2_EN (1U << 9) +/* PadLock Hash Engine */ +#define CPUID_C000_0001_EDX_PHE (1U << 10) +/* PHE enabled */ +#define CPUID_C000_0001_EDX_PHE_EN (1U << 11) +/* PadLock Montgomery Multiplier */ +#define CPUID_C000_0001_EDX_PMM (1U << 12) +/* PMM enabled */ +#define CPUID_C000_0001_EDX_PMM_EN (1U << 13) + #define CPUID_VENDOR_SZ 12 #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ @@ -1122,7 +1143,16 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_VENDOR_AMD_3 0x444d4163 /* "cAMD" */ #define CPUID_VENDOR_AMD "AuthenticAMD" -#define CPUID_VENDOR_VIA "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN1_1 0x746E6543 /* "Cent" */ +#define CPUID_VENDOR_ZHAOXIN1_2 0x48727561 /* "aurH" */ +#define CPUID_VENDOR_ZHAOXIN1_3 0x736C7561 /* "auls" */ + +#define CPUID_VENDOR_ZHAOXIN2_1 0x68532020 /* " Sh" */ +#define CPUID_VENDOR_ZHAOXIN2_2 0x68676E61 /* "angh" */ +#define CPUID_VENDOR_ZHAOXIN2_3 0x20206961 /* "ai " */ + +#define CPUID_VENDOR_ZHAOXIN1 "CentaurHauls" +#define CPUID_VENDOR_ZHAOXIN2 " Shanghai " #define CPUID_VENDOR_HYGON "HygonGenuine" @@ -1132,6 +1162,15 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define IS_AMD_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && \ (env)->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && \ (env)->cpuid_vendor3 == CPUID_VENDOR_AMD_3) +#define IS_ZHAOXIN1_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN1_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN1_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN1_3) +#define IS_ZHAOXIN2_CPU(env) \ + ((env)->cpuid_vendor1 == CPUID_VENDOR_ZHAOXIN2_1 && \ + (env)->cpuid_vendor2 == CPUID_VENDOR_ZHAOXIN2_2 && \ + (env)->cpuid_vendor3 == CPUID_VENDOR_ZHAOXIN2_3) +#define IS_ZHAOXIN_CPU(env) (IS_ZHAOXIN1_CPU(env) || IS_ZHAOXIN2_CPU(env)) #define CPUID_MWAIT_IBE (1U << 1) /* Interrupts can exit capability */ #define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */ diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index e99c02cd4b..044ad236ae 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -18,7 +18,9 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); -void hvf_handle_io(CPUArchState *, uint16_t, void *, int, int, int); +void hvf_handle_io(CPUState *, uint16_t, void *, int, int, int); +void hvf_simulate_rdmsr(CPUX86State *env); +void hvf_simulate_wrmsr(CPUX86State *env); /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index ca08f0753f..9ba0e04ac7 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -61,6 +61,7 @@ #include "vmx.h" #include "x86.h" #include "x86_descr.h" +#include "x86_flags.h" #include "x86_mmu.h" #include "x86_decode.h" #include "x86_emu.h" @@ -103,7 +104,7 @@ static void update_apic_tpr(CPUState *cpu) #define VECTORING_INFO_VECTOR_MASK 0xff -void hvf_handle_io(CPUArchState *env, uint16_t port, void *buffer, +void hvf_handle_io(CPUState *env, uint16_t port, void *buffer, int direction, int size, int count) { int i; @@ -434,6 +435,264 @@ static void hvf_cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } +void hvf_load_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); + RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); + RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); + RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); + RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); + RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); + RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); + RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); + for (i = 8; i < 16; i++) { + RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); + } + + env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); + rflags_to_lflags(env); + env->eip = rreg(cs->accel->fd, HV_X86_RIP); +} + +void hvf_store_regs(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + + int i = 0; + wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); + wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); + wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); + wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); + wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); + wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); + wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); + wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); + for (i = 8; i < 16; i++) { + wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); + } + + lflags_to_rflags(env); + wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); + macvm_set_rip(cs, env->eip); +} + +void hvf_simulate_rdmsr(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + uint32_t msr = ECX(env); + uint64_t val = 0; + + switch (msr) { + case MSR_IA32_TSC: + val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); + break; + case MSR_IA32_APICBASE: + val = cpu_get_apic_base(cpu->apic_state); + break; + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_read(index, &val); + if (ret < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_IA32_UCODE_REV: + val = cpu->ucode_rev; + break; + case MSR_EFER: + val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); + break; + case MSR_FSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); + break; + case MSR_GSBASE: + val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); + break; + case MSR_KERNELGSBASE: + val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_IA32_MISC_ENABLE: + val = env->msr_ia32_misc_enable; + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; + break; + case MSR_MTRRfix64K_00000: + val = env->mtrr_fixed[0]; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; + break; + case MSR_MTRRdefType: + val = env->mtrr_deftype; + break; + case MSR_CORE_THREAD_COUNT: + val = cpu_x86_get_msr_core_thread_count(cpu); + break; + default: + /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ + val = 0; + break; + } + + RAX(env) = (uint32_t)val; + RDX(env) = (uint32_t)(val >> 32); +} + +void hvf_simulate_wrmsr(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + uint32_t msr = ECX(env); + uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); + + switch (msr) { + case MSR_IA32_TSC: + break; + case MSR_IA32_APICBASE: { + int r; + + r = cpu_set_apic_base(cpu->apic_state, data); + if (r < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_APIC_START ... MSR_APIC_END: { + int ret; + int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; + + ret = apic_msr_write(index, data); + if (ret < 0) { + x86_emul_raise_exception(env, EXCP0D_GPF, 0); + } + + break; + } + case MSR_FSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); + break; + case MSR_GSBASE: + wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); + break; + case MSR_KERNELGSBASE: + wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); + break; + case MSR_STAR: + abort(); + break; + case MSR_LSTAR: + abort(); + break; + case MSR_CSTAR: + abort(); + break; + case MSR_EFER: + /*printf("new efer %llx\n", EFER(cs));*/ + wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); + if (data & MSR_EFER_NXE) { + hv_vcpu_invalidate_tlb(cs->accel->fd); + } + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; + break; + case MSR_MTRRfix64K_00000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; + break; + case MSR_MTRRdefType: + env->mtrr_deftype = data; + break; + default: + break; + } + + /* Related to support known hypervisor interface */ + /* if (g_hypervisor_iface) + g_hypervisor_iface->wrmsr_handler(cs, msr, data); + + printf("write msr %llx\n", RCX(cs));*/ +} + int hvf_vcpu_exec(CPUState *cpu) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -517,10 +776,10 @@ int hvf_vcpu_exec(CPUState *cpu) if (ept_emulation_fault(slot, gpa, exit_qual)) { struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } break; @@ -535,8 +794,8 @@ int hvf_vcpu_exec(CPUState *cpu) if (!string && in) { uint64_t val = 0; - load_regs(cpu); - hvf_handle_io(env, port, &val, 0, size, 1); + hvf_load_regs(cpu); + hvf_handle_io(env_cpu(env), port, &val, 0, size, 1); if (size == 1) { AL(env) = val; } else if (size == 2) { @@ -547,21 +806,21 @@ int hvf_vcpu_exec(CPUState *cpu) RAX(env) = (uint64_t)val; } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } else if (!string && !in) { RAX(env) = rreg(cpu->accel->fd, HV_X86_RAX); - hvf_handle_io(env, port, &RAX(env), 1, size, 1); + hvf_handle_io(env_cpu(env), port, &RAX(env), 1, size, 1); macvm_set_rip(cpu, rip + ins_len); break; } struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); assert(ins_len == decode.len); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } @@ -614,21 +873,21 @@ int hvf_vcpu_exec(CPUState *cpu) case EXIT_REASON_RDMSR: case EXIT_REASON_WRMSR: { - load_regs(cpu); + hvf_load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { - simulate_rdmsr(env); + hvf_simulate_rdmsr(env); } else { - simulate_wrmsr(env); + hvf_simulate_wrmsr(env); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_CR_ACCESS: { int cr; int reg; - load_regs(cpu); + hvf_load_regs(cpu); cr = exit_qual & 15; reg = (exit_qual >> 8) & 15; @@ -656,16 +915,16 @@ int hvf_vcpu_exec(CPUState *cpu) abort(); } env->eip += ins_len; - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_APIC_ACCESS: { /* TODO */ struct x86_decode decode; - load_regs(cpu); + hvf_load_regs(cpu); decode_instruction(env, &decode); exec_instruction(env, &decode); - store_regs(cpu); + hvf_store_regs(cpu); break; } case EXIT_REASON_TPR: { @@ -674,7 +933,7 @@ int hvf_vcpu_exec(CPUState *cpu) } case EXIT_REASON_TASK_SWITCH: { uint64_t vinfo = rvmcs(cpu->accel->fd, VMCS_IDT_VECTORING_INFO); - x68_segment_selector sel = {.sel = exit_qual & 0xffff}; + x86_segment_selector sel = {.sel = exit_qual & 0xffff}; vmx_handle_task_switch(cpu, sel, (exit_qual >> 30) & 0x3, vinfo & VMCS_INTR_VALID, vinfo & VECTORING_INFO_VECTOR_MASK, vinfo & VMCS_INTR_T_MASK); diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index 80e36136d0..a0ede13886 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -48,7 +48,7 @@ bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel) + x86_segment_selector sel) { target_ulong base; uint32_t limit; @@ -78,7 +78,7 @@ bool x86_read_segment_descriptor(CPUState *cpu, bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel) + x86_segment_selector sel) { target_ulong base; uint32_t limit; diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h index 3570f29aa9..063cd0b83e 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/hvf/x86.h @@ -183,7 +183,7 @@ static inline uint32_t x86_call_gate_offset(x86_call_gate *gate) #define GDT_SEL 0 #define LDT_SEL 1 -typedef struct x68_segment_selector { +typedef struct x86_segment_selector { union { uint16_t sel; struct { @@ -192,7 +192,7 @@ typedef struct x68_segment_selector { uint16_t index:13; }; }; -} __attribute__ ((__packed__)) x68_segment_selector; +} __attribute__ ((__packed__)) x86_segment_selector; /* useful register access macros */ #define x86_reg(cpu, reg) ((x86_register *) &cpu->regs[reg]) @@ -250,10 +250,10 @@ typedef struct x68_segment_selector { /* deal with GDT/LDT descriptors in memory */ bool x86_read_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel); + x86_segment_selector sel); bool x86_write_segment_descriptor(CPUState *cpu, struct x86_segment_descriptor *desc, - x68_segment_selector sel); + x86_segment_selector sel); bool x86_read_call_gate(CPUState *cpu, struct x86_call_gate *idt_desc, int gate); diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index a4a28f113f..d6d5894e54 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -1893,6 +1893,16 @@ static void decode_prefix(CPUX86State *env, struct x86_decode *decode) } } +static struct x86_segment_descriptor get_cs_descriptor(CPUState *s) +{ + struct vmx_segment vmx_cs; + x86_segment_descriptor cs; + vmx_read_segment_descriptor(s, &vmx_cs, R_CS); + vmx_segment_to_x86_descriptor(s, &vmx_cs, &cs); + + return cs; +} + void set_addressing_size(CPUX86State *env, struct x86_decode *decode) { decode->addressing_size = -1; @@ -1904,10 +1914,9 @@ void set_addressing_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); + x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); /* check db */ - if ((cs.ar >> 14) & 1) { + if (cs.db) { if (decode->addr_size_override) { decode->addressing_size = 2; } else { @@ -1941,10 +1950,9 @@ void set_operand_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - struct vmx_segment cs; - vmx_read_segment_descriptor(env_cpu(env), &cs, R_CS); + x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); /* check db */ - if ((cs.ar >> 14) & 1) { + if (cs.db) { if (decode->op_size_override) { decode->operand_size = 2; } else{ diff --git a/target/i386/hvf/x86_descr.c b/target/i386/hvf/x86_descr.c index f33836d6cb..7b599c9037 100644 --- a/target/i386/hvf/x86_descr.c +++ b/target/i386/hvf/x86_descr.c @@ -60,14 +60,14 @@ uint64_t vmx_read_segment_base(CPUState *cpu, X86Seg seg) return rvmcs(cpu->accel->fd, vmx_segment_fields[seg].base); } -x68_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, X86Seg seg) { - x68_segment_selector sel; + x86_segment_selector sel; sel.sel = rvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector); return sel; } -void vmx_write_segment_selector(CPUState *cpu, x68_segment_selector selector, X86Seg seg) +void vmx_write_segment_selector(CPUState *cpu, x86_segment_selector selector, X86Seg seg) { wvmcs(cpu->accel->fd, vmx_segment_fields[seg].selector, selector.sel); } @@ -90,7 +90,7 @@ void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, X86Se wvmcs(cpu->accel->fd, sf->ar_bytes, desc->ar); } -void x86_segment_descriptor_to_vmx(CPUState *cpu, x68_segment_selector selector, +void x86_segment_descriptor_to_vmx(CPUState *cpu, x86_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc) { diff --git a/target/i386/hvf/x86_descr.h b/target/i386/hvf/x86_descr.h index 9f06014b56..ce5de98349 100644 --- a/target/i386/hvf/x86_descr.h +++ b/target/i386/hvf/x86_descr.h @@ -34,10 +34,10 @@ void vmx_read_segment_descriptor(CPUState *cpu, void vmx_write_segment_descriptor(CPUState *cpu, struct vmx_segment *desc, enum X86Seg seg); -x68_segment_selector vmx_read_segment_selector(CPUState *cpu, +x86_segment_selector vmx_read_segment_selector(CPUState *cpu, enum X86Seg seg); void vmx_write_segment_selector(CPUState *cpu, - x68_segment_selector selector, + x86_segment_selector selector, enum X86Seg seg); uint64_t vmx_read_segment_base(CPUState *cpu, enum X86Seg seg); @@ -45,7 +45,7 @@ void vmx_write_segment_base(CPUState *cpu, enum X86Seg seg, uint64_t base); void x86_segment_descriptor_to_vmx(CPUState *cpu, - x68_segment_selector selector, + x86_segment_selector selector, struct x86_segment_descriptor *desc, struct vmx_segment *vmx_desc); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 69c61c9c07..ebba80a36b 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -44,9 +44,7 @@ #include "x86_flags.h" #include "vmcs.h" #include "vmx.h" - -void hvf_handle_io(CPUState *cs, uint16_t port, void *data, - int direction, int size, uint32_t count); +#include "hvf-i386.h" #define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ { \ @@ -663,8 +661,7 @@ static void exec_lods(CPUX86State *env, struct x86_decode *decode) env->eip += decode->len; } -static void raise_exception(CPUX86State *env, int exception_index, - int error_code) +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code) { env->exception_nr = exception_index; env->error_code = error_code; @@ -672,227 +669,15 @@ static void raise_exception(CPUX86State *env, int exception_index, env->exception_injected = 1; } -void simulate_rdmsr(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - uint32_t msr = ECX(env); - uint64_t val = 0; - - switch (msr) { - case MSR_IA32_TSC: - val = rdtscp() + rvmcs(cs->accel->fd, VMCS_TSC_OFFSET); - break; - case MSR_IA32_APICBASE: - val = cpu_get_apic_base(cpu->apic_state); - break; - case MSR_APIC_START ... MSR_APIC_END: { - int ret; - int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - - ret = apic_msr_read(index, &val); - if (ret < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_IA32_UCODE_REV: - val = cpu->ucode_rev; - break; - case MSR_EFER: - val = rvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER); - break; - case MSR_FSBASE: - val = rvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE); - break; - case MSR_GSBASE: - val = rvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE); - break; - case MSR_KERNELGSBASE: - val = rvmcs(cs->accel->fd, VMCS_HOST_FS_BASE); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_IA32_MISC_ENABLE: - val = env->msr_ia32_misc_enable; - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - val = env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask; - break; - case MSR_MTRRfix64K_00000: - val = env->mtrr_fixed[0]; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1]; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - val = env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3]; - break; - case MSR_MTRRdefType: - val = env->mtrr_deftype; - break; - case MSR_CORE_THREAD_COUNT: - val = cpu_x86_get_msr_core_thread_count(cpu); - break; - default: - /* fprintf(stderr, "%s: unknown msr 0x%x\n", __func__, msr); */ - val = 0; - break; - } - - RAX(env) = (uint32_t)val; - RDX(env) = (uint32_t)(val >> 32); -} - static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_rdmsr(env); + hvf_simulate_rdmsr(env); env->eip += decode->len; } -void simulate_wrmsr(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - uint32_t msr = ECX(env); - uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); - - switch (msr) { - case MSR_IA32_TSC: - break; - case MSR_IA32_APICBASE: { - int r; - - r = cpu_set_apic_base(cpu->apic_state, data); - if (r < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_APIC_START ... MSR_APIC_END: { - int ret; - int index = (uint32_t)env->regs[R_ECX] - MSR_APIC_START; - - ret = apic_msr_write(index, data); - if (ret < 0) { - raise_exception(env, EXCP0D_GPF, 0); - } - - break; - } - case MSR_FSBASE: - wvmcs(cs->accel->fd, VMCS_GUEST_FS_BASE, data); - break; - case MSR_GSBASE: - wvmcs(cs->accel->fd, VMCS_GUEST_GS_BASE, data); - break; - case MSR_KERNELGSBASE: - wvmcs(cs->accel->fd, VMCS_HOST_FS_BASE, data); - break; - case MSR_STAR: - abort(); - break; - case MSR_LSTAR: - abort(); - break; - case MSR_CSTAR: - abort(); - break; - case MSR_EFER: - /*printf("new efer %llx\n", EFER(cs));*/ - wvmcs(cs->accel->fd, VMCS_GUEST_IA32_EFER, data); - if (data & MSR_EFER_NXE) { - hv_vcpu_invalidate_tlb(cs->accel->fd); - } - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysBase(0)) / 2].base = data; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - env->mtrr_var[(ECX(env) - MSR_MTRRphysMask(0)) / 2].mask = data; - break; - case MSR_MTRRfix64K_00000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix64K_00000] = data; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix16K_80000 + 1] = data; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - env->mtrr_fixed[ECX(env) - MSR_MTRRfix4K_C0000 + 3] = data; - break; - case MSR_MTRRdefType: - env->mtrr_deftype = data; - break; - default: - break; - } - - /* Related to support known hypervisor interface */ - /* if (g_hypervisor_iface) - g_hypervisor_iface->wrmsr_handler(cs, msr, data); - - printf("write msr %llx\n", RCX(cs));*/ -} - static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) { - simulate_wrmsr(env); + hvf_simulate_wrmsr(env); env->eip += decode->len; } @@ -1454,58 +1239,8 @@ static void init_cmd_handler(void) } } -void load_regs(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - int i = 0; - RRX(env, R_EAX) = rreg(cs->accel->fd, HV_X86_RAX); - RRX(env, R_EBX) = rreg(cs->accel->fd, HV_X86_RBX); - RRX(env, R_ECX) = rreg(cs->accel->fd, HV_X86_RCX); - RRX(env, R_EDX) = rreg(cs->accel->fd, HV_X86_RDX); - RRX(env, R_ESI) = rreg(cs->accel->fd, HV_X86_RSI); - RRX(env, R_EDI) = rreg(cs->accel->fd, HV_X86_RDI); - RRX(env, R_ESP) = rreg(cs->accel->fd, HV_X86_RSP); - RRX(env, R_EBP) = rreg(cs->accel->fd, HV_X86_RBP); - for (i = 8; i < 16; i++) { - RRX(env, i) = rreg(cs->accel->fd, HV_X86_RAX + i); - } - - env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); - rflags_to_lflags(env); - env->eip = rreg(cs->accel->fd, HV_X86_RIP); -} - -void store_regs(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - - int i = 0; - wreg(cs->accel->fd, HV_X86_RAX, RAX(env)); - wreg(cs->accel->fd, HV_X86_RBX, RBX(env)); - wreg(cs->accel->fd, HV_X86_RCX, RCX(env)); - wreg(cs->accel->fd, HV_X86_RDX, RDX(env)); - wreg(cs->accel->fd, HV_X86_RSI, RSI(env)); - wreg(cs->accel->fd, HV_X86_RDI, RDI(env)); - wreg(cs->accel->fd, HV_X86_RBP, RBP(env)); - wreg(cs->accel->fd, HV_X86_RSP, RSP(env)); - for (i = 8; i < 16; i++) { - wreg(cs->accel->fd, HV_X86_RAX + i, RRX(env, i)); - } - - lflags_to_rflags(env); - wreg(cs->accel->fd, HV_X86_RFLAGS, env->eflags); - macvm_set_rip(cs, env->eip); -} - bool exec_instruction(CPUX86State *env, struct x86_decode *ins) { - /*if (hvf_vcpu_id(cs)) - printf("%d, %llx: exec_instruction %s\n", hvf_vcpu_id(cs), env->eip, - decode_cmd_to_string(ins->cmd));*/ - if (!_cmd_handler[ins->cmd].handler) { printf("Unimplemented handler (%llx) for %d (%x %x) \n", env->eip, ins->cmd, ins->opcode[0], diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 8bd97608c4..bc0fc72c76 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -25,12 +25,7 @@ void init_emu(void); bool exec_instruction(CPUX86State *env, struct x86_decode *ins); - -void load_regs(CPUState *cpu); -void store_regs(CPUState *cpu); - -void simulate_rdmsr(CPUX86State *env); -void simulate_wrmsr(CPUX86State *env); +void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code); target_ulong read_reg(CPUX86State *env, int reg, int size); void write_reg(CPUX86State *env, int reg, target_ulong val, int size); diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index bcd844cff6..161217991f 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -76,16 +76,16 @@ static void load_state_from_tss32(CPUState *cpu, struct x86_tss_segment32 *tss) RSI(env) = tss->esi; RDI(env) = tss->edi; - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ldt}}, R_LDTR); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->es}}, R_ES); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->cs}}, R_CS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ss}}, R_SS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->ds}}, R_DS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->fs}}, R_FS); - vmx_write_segment_selector(cpu, (x68_segment_selector){{tss->gs}}, R_GS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ldt}}, R_LDTR); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->es}}, R_ES); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->cs}}, R_CS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ss}}, R_SS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->ds}}, R_DS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->fs}}, R_FS); + vmx_write_segment_selector(cpu, (x86_segment_selector){{tss->gs}}, R_GS); } -static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segment_selector old_tss_sel, +static int task_switch_32(CPUState *cpu, x86_segment_selector tss_sel, x86_segment_selector old_tss_sel, uint64_t old_tss_base, struct x86_segment_descriptor *new_desc) { struct x86_tss_segment32 tss_seg; @@ -108,7 +108,7 @@ static int task_switch_32(CPUState *cpu, x68_segment_selector tss_sel, x68_segme return 0; } -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type) { uint64_t rip = rreg(cpu->accel->fd, HV_X86_RIP); if (!gate_valid || (gate_type != VMCS_INTR_T_HWEXCEPTION && @@ -119,10 +119,10 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea return; } - load_regs(cpu); + hvf_load_regs(cpu); struct x86_segment_descriptor curr_tss_desc, next_tss_desc; - x68_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); + x86_segment_selector old_tss_sel = vmx_read_segment_selector(cpu, R_TR); uint64_t old_tss_base = vmx_read_segment_base(cpu, R_TR); uint32_t desc_limit; struct x86_call_gate task_gate_desc; @@ -140,7 +140,7 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea x86_read_call_gate(cpu, &task_gate_desc, gate); dpl = task_gate_desc.dpl; - x68_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); + x86_segment_selector cs = vmx_read_segment_selector(cpu, R_CS); if (tss_sel.rpl > dpl || cs.rpl > dpl) ;//DPRINTF("emulate_gp"); } @@ -178,7 +178,7 @@ void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, int rea x86_segment_descriptor_to_vmx(cpu, tss_sel, &next_tss_desc, &vmx_seg); vmx_write_segment_descriptor(cpu, &vmx_seg, R_TR); - store_regs(cpu); + hvf_store_regs(cpu); hv_vcpu_invalidate_tlb(cpu->accel->fd); } diff --git a/target/i386/hvf/x86_task.h b/target/i386/hvf/x86_task.h index 4eaa61a7de..b9afac6a47 100644 --- a/target/i386/hvf/x86_task.h +++ b/target/i386/hvf/x86_task.h @@ -15,6 +15,6 @@ #ifndef HVF_X86_TASK_H #define HVF_X86_TASK_H -void vmx_handle_task_switch(CPUState *cpu, x68_segment_selector tss_sel, +void vmx_handle_task_switch(CPUState *cpu, x86_segment_selector tss_sel, int reason, bool gate_valid, uint8_t gate, uint64_t gate_type); #endif diff --git a/target/i386/hvf/x86hvf.h b/target/i386/hvf/x86hvf.h index 423a89b6ad..8c46ce8ad0 100644 --- a/target/i386/hvf/x86hvf.h +++ b/target/i386/hvf/x86hvf.h @@ -31,4 +31,7 @@ void hvf_get_xsave(CPUState *cs); void hvf_get_msrs(CPUState *cs); void vmx_clear_int_window_exiting(CPUState *cs); void vmx_update_tpr(CPUState *cs); + +void hvf_load_regs(CPUState *cpu); +void hvf_store_regs(CPUState *cpu); #endif diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index f112c6c673..4858ae9a5f 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -1141,7 +1141,7 @@ void helper_f2xm1(CPUX86State *env) int32_t exp = extractFloatx80Exp(ST0); bool sign = extractFloatx80Sign(ST0); - if (floatx80_invalid_encoding(ST0)) { + if (floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -1383,8 +1383,8 @@ void helper_fpatan(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -1393,7 +1393,8 @@ void helper_fpatan(CPUX86State *env) /* Pass this NaN through. */ } else if (floatx80_is_zero(ST1) && !arg0_sign) { /* Pass this zero through. */ - } else if (((floatx80_is_infinity(ST0) && !floatx80_is_infinity(ST1)) || + } else if (((floatx80_is_infinity(ST0, &env->fp_status) && + !floatx80_is_infinity(ST1, &env->fp_status)) || arg0_exp - arg1_exp >= 80) && !arg0_sign) { /* @@ -1442,8 +1443,8 @@ void helper_fpatan(CPUX86State *env) rexp = pi_exp; rsig0 = pi_sig_high; rsig1 = pi_sig_low; - } else if (floatx80_is_infinity(ST1)) { - if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST1, &env->fp_status)) { + if (floatx80_is_infinity(ST0, &env->fp_status)) { if (arg0_sign) { rexp = pi_34_exp; rsig0 = pi_34_sig_high; @@ -1462,7 +1463,8 @@ void helper_fpatan(CPUX86State *env) rexp = pi_2_exp; rsig0 = pi_2_sig_high; rsig1 = pi_2_sig_low; - } else if (floatx80_is_infinity(ST0) || arg0_exp - arg1_exp >= 80) { + } else if (floatx80_is_infinity(ST0, &env->fp_status) || + arg0_exp - arg1_exp >= 80) { /* ST0 is negative. */ rexp = pi_exp; rsig0 = pi_sig_high; @@ -1817,7 +1819,7 @@ void helper_fxtract(CPUX86State *env) &env->fp_status); fpush(env); ST0 = temp.d; - } else if (floatx80_invalid_encoding(ST0)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); fpush(env); @@ -1829,10 +1831,10 @@ void helper_fxtract(CPUX86State *env) } fpush(env); ST0 = ST1; - } else if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST0, &env->fp_status)) { fpush(env); ST0 = ST1; - ST1 = floatx80_infinity; + ST1 = floatx80_default_inf(0, &env->fp_status); } else { int expdif; @@ -1868,7 +1870,8 @@ static void helper_fprem_common(CPUX86State *env, bool mod) env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ if (floatx80_is_zero(ST0) || floatx80_is_zero(ST1) || exp0 == 0x7fff || exp1 == 0x7fff || - floatx80_invalid_encoding(ST0) || floatx80_invalid_encoding(ST1)) { + floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { ST0 = floatx80_modrem(ST0, ST1, mod, "ient, &env->fp_status); } else { if (exp0 == 0) { @@ -2064,8 +2067,8 @@ void helper_fyl2xp1(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -2162,8 +2165,8 @@ void helper_fyl2x(CPUX86State *env) } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { + } else if (floatx80_invalid_encoding(ST0, &env->fp_status) || + floatx80_invalid_encoding(ST1, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST0)) { @@ -2173,7 +2176,7 @@ void helper_fyl2x(CPUX86State *env) } else if (arg0_sign && !floatx80_is_zero(ST0)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_infinity(ST1)) { + } else if (floatx80_is_infinity(ST1, &env->fp_status)) { FloatRelation cmp = floatx80_compare(ST0, floatx80_one, &env->fp_status); switch (cmp) { @@ -2188,7 +2191,7 @@ void helper_fyl2x(CPUX86State *env) ST1 = floatx80_default_nan(&env->fp_status); break; } - } else if (floatx80_is_infinity(ST0)) { + } else if (floatx80_is_infinity(ST0, &env->fp_status)) { if (floatx80_is_zero(ST1)) { float_raise(float_flag_invalid, &env->fp_status); ST1 = floatx80_default_nan(&env->fp_status); @@ -2329,7 +2332,8 @@ void helper_frndint(CPUX86State *env) void helper_fscale(CPUX86State *env) { uint8_t old_flags = save_exception_flags(env); - if (floatx80_invalid_encoding(ST1) || floatx80_invalid_encoding(ST0)) { + if (floatx80_invalid_encoding(ST1, &env->fp_status) || + floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else if (floatx80_is_any_nan(ST1)) { @@ -2341,11 +2345,11 @@ void helper_fscale(CPUX86State *env) float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_silence_nan(ST0, &env->fp_status); } - } else if (floatx80_is_infinity(ST1) && - !floatx80_invalid_encoding(ST0) && + } else if (floatx80_is_infinity(ST1, &env->fp_status) && + !floatx80_invalid_encoding(ST0, &env->fp_status) && !floatx80_is_any_nan(ST0)) { if (floatx80_is_neg(ST1)) { - if (floatx80_is_infinity(ST0)) { + if (floatx80_is_infinity(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else { @@ -2358,9 +2362,8 @@ void helper_fscale(CPUX86State *env) float_raise(float_flag_invalid, &env->fp_status); ST0 = floatx80_default_nan(&env->fp_status); } else { - ST0 = (floatx80_is_neg(ST0) ? - floatx80_chs(floatx80_infinity) : - floatx80_infinity); + ST0 = floatx80_default_inf(floatx80_is_neg(ST0), + &env->fp_status); } } } else { diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index e91f4a5239..3788f895c1 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -406,7 +406,7 @@ static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); CPULoongArchState *env = &cpu->env; - uint32_t data = 0; + uint32_t data = 0, field; int i; for (i = 0; i < 21; i++) { @@ -419,7 +419,13 @@ static void loongarch_la464_initfn(Object *obj) data = FIELD_DP32(data, CPUCFG1, ARCH, 2); data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); - data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f); + if (kvm_enabled()) { + /* GPA address width of VM is 47, field value is 47 - 1 */ + field = 0x2e; + } else { + field = 0x2f; /* 48 bit - 1 */ + } + data = FIELD_DP32(data, CPUCFG1, PALEN, field); data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f); data = FIELD_DP32(data, CPUCFG1, UAL, 1); data = FIELD_DP32(data, CPUCFG1, RI, 1); @@ -712,34 +718,12 @@ static void loongarch_set_lasx(Object *obj, bool value, Error **errp) cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); } -static bool loongarch_get_lbt(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lbt != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lbt(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - - cpu->lbt = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; -} - -static bool loongarch_get_pmu(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_pmu(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - - cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; -} - void loongarch_cpu_post_init(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); + cpu->lbt = ON_OFF_AUTO_OFF; + cpu->pmu = ON_OFF_AUTO_OFF; cpu->lsx = ON_OFF_AUTO_AUTO; cpu->lasx = ON_OFF_AUTO_AUTO; object_property_add_bool(obj, "lsx", loongarch_get_lsx, @@ -748,21 +732,7 @@ void loongarch_cpu_post_init(Object *obj) loongarch_set_lasx); /* lbt is enabled only in kvm mode, not supported in tcg mode */ if (kvm_enabled()) { - cpu->lbt = ON_OFF_AUTO_AUTO; - object_property_add_bool(obj, "lbt", loongarch_get_lbt, - loongarch_set_lbt); - object_property_set_description(obj, "lbt", - "Set off to disable Binary Tranlation."); - - cpu->pmu = ON_OFF_AUTO_AUTO; - object_property_add_bool(obj, "pmu", loongarch_get_pmu, - loongarch_set_pmu); - object_property_set_description(obj, "pmu", - "Set off to performance monitor unit."); - - } else { - cpu->lbt = ON_OFF_AUTO_OFF; - cpu->pmu = ON_OFF_AUTO_OFF; + kvm_loongarch_cpu_post_init(cpu); } } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index f2a23b7a43..83183a33ab 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -287,6 +287,8 @@ enum loongarch_features { LOONGARCH_FEATURE_LASX, LOONGARCH_FEATURE_LBT, /* loongson binary translation extension */ LOONGARCH_FEATURE_PMU, + LOONGARCH_FEATURE_PV_IPI, + LOONGARCH_FEATURE_STEALTIME, }; typedef struct LoongArchBT { @@ -310,6 +312,7 @@ typedef struct CPUArchState { lbt_t lbt; uint32_t cpucfg[21]; + uint32_t pv_features; /* LoongArch CSRs */ uint64_t CSR_CRMD; @@ -406,6 +409,8 @@ struct ArchCPU { OnOffAuto pmu; OnOffAuto lsx; OnOffAuto lasx; + OnOffAuto kvm_pv_ipi; + OnOffAuto kvm_steal_time; /* 'compatible' string for this CPU for Linux device trees */ const char *dtb_compatible; @@ -491,4 +496,12 @@ static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, void loongarch_cpu_post_init(Object *obj); +#ifdef CONFIG_KVM +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); +#else +static inline void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) +{ +} +#endif + #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/gdbstub.c b/target/loongarch/gdbstub.c index dafa4feb75..471eda28c7 100644 --- a/target/loongarch/gdbstub.c +++ b/target/loongarch/gdbstub.c @@ -63,23 +63,24 @@ int loongarch_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) { CPULoongArchState *env = cpu_env(cs); target_ulong tmp; - int read_length; int length = 0; + if (n < 0 || n > 34) { + return 0; + } + if (is_la64(env)) { tmp = ldq_le_p(mem_buf); - read_length = 8; + length = 8; } else { tmp = ldl_le_p(mem_buf); - read_length = 4; + length = 4; } if (0 <= n && n < 32) { env->gpr[n] = tmp; - length = read_length; } else if (n == 33) { set_pc(env, tmp); - length = read_length; } return length; } diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index a3f55155b0..28735c80be 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include <sys/ioctl.h> #include <linux/kvm.h> - +#include "asm-loongarch/kvm_para.h" #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/error-report.h" @@ -21,6 +21,7 @@ #include "exec/address-spaces.h" #include "hw/boards.h" #include "hw/irq.h" +#include "hw/loongarch/virt.h" #include "qemu/log.h" #include "hw/loader.h" #include "system/runstate.h" @@ -83,6 +84,33 @@ static int kvm_set_stealtime(CPUState *cs) return 0; } +static int kvm_set_pv_features(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + int err; + uint64_t val; + struct kvm_device_attr attr = { + .group = KVM_LOONGARCH_VCPU_CPUCFG, + .attr = CPUCFG_KVM_FEATURE, + .addr = (uint64_t)&val, + }; + + err = kvm_vcpu_ioctl(cs, KVM_HAS_DEVICE_ATTR, attr); + if (err) { + return 0; + } + + val = env->pv_features; + err = kvm_vcpu_ioctl(cs, KVM_SET_DEVICE_ATTR, attr); + if (err) { + error_report("Fail to set pv feature "TARGET_FMT_lx " with error %s", + val, strerror(errno)); + return err; + } + + return 0; +} + static int kvm_loongarch_get_regs_core(CPUState *cs) { int ret = 0; @@ -581,9 +609,16 @@ static int kvm_loongarch_get_lbt(CPUState *cs) void kvm_arch_reset_vcpu(CPUState *cs) { CPULoongArchState *env = cpu_env(cs); + int ret = 0; + uint64_t unused = 0; env->mp_state = KVM_MP_STATE_RUNNABLE; - kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, 0); + ret = kvm_set_one_reg(cs, KVM_REG_LOONGARCH_VCPU_RESET, &unused); + if (ret) { + error_report("Failed to set KVM_REG_LOONGARCH_VCPU_RESET: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } } static int kvm_loongarch_get_mpstate(CPUState *cs) @@ -731,6 +766,7 @@ int kvm_arch_get_registers(CPUState *cs, Error **errp) int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) { int ret; + static int once; ret = kvm_loongarch_put_regs_core(cs); if (ret) { @@ -757,6 +793,14 @@ int kvm_arch_put_registers(CPUState *cs, int level, Error **errp) return ret; } + if (!once) { + ret = kvm_set_pv_features(cs); + if (ret) { + return ret; + } + once = 1; + } + if (level >= KVM_PUT_FULL_STATE) { /* * only KVM_PUT_FULL_STATE is required, kvm kernel will clear @@ -875,6 +919,18 @@ static bool kvm_feature_supported(CPUState *cs, enum loongarch_features feature) ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); return (ret == 0); + case LOONGARCH_FEATURE_PV_IPI: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_PV_IPI; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + + case LOONGARCH_FEATURE_STEALTIME: + attr.group = KVM_LOONGARCH_VM_FEAT_CTRL; + attr.attr = KVM_LOONGARCH_VM_FEAT_PV_STEALTIME; + ret = kvm_vm_ioctl(kvm_state, KVM_HAS_DEVICE_ATTR, &attr); + return (ret == 0); + default: return false; } @@ -973,6 +1029,52 @@ static int kvm_cpu_check_pmu(CPUState *cs, Error **errp) return 0; } +static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + CPULoongArchState *env = cpu_env(cs); + bool kvm_supported; + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_PV_IPI); + if (cpu->kvm_pv_ipi == ON_OFF_AUTO_ON) { + if (!kvm_supported) { + error_setg(errp, "'pv_ipi' feature not supported by KVM host"); + return -ENOTSUP; + } + } else if (cpu->kvm_pv_ipi != ON_OFF_AUTO_AUTO) { + kvm_supported = false; + } + + if (kvm_supported) { + env->pv_features |= BIT(KVM_FEATURE_IPI); + } + + kvm_supported = kvm_feature_supported(cs, LOONGARCH_FEATURE_STEALTIME); + if (cpu->kvm_steal_time == ON_OFF_AUTO_ON) { + if (!kvm_supported) { + error_setg(errp, "'kvm stealtime' feature not supported by KVM host"); + return -ENOTSUP; + } + } else if (cpu->kvm_steal_time != ON_OFF_AUTO_AUTO) { + kvm_supported = false; + } + + if (kvm_supported) { + env->pv_features |= BIT(KVM_FEATURE_STEAL_TIME); + } + + if (object_dynamic_cast(OBJECT(ms), TYPE_LOONGARCH_VIRT_MACHINE)) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + + if (virt_is_veiointc_enabled(lvms)) { + env->pv_features |= BIT(KVM_FEATURE_VIRT_EXTIOI); + } + } + + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { uint64_t val; @@ -1006,9 +1108,89 @@ int kvm_arch_init_vcpu(CPUState *cs) error_report_err(local_err); } + ret = kvm_cpu_check_pv_features(cs, &local_err); + if (ret < 0) { + error_report_err(local_err); + } + return ret; } +static bool loongarch_get_lbt(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lbt != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lbt(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->lbt = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +static bool loongarch_get_pmu(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->pmu != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_pmu(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->pmu = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +static bool kvm_pv_ipi_get(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->kvm_pv_ipi != ON_OFF_AUTO_OFF; +} + +static void kvm_pv_ipi_set(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->kvm_pv_ipi = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +static bool kvm_steal_time_get(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->kvm_steal_time != ON_OFF_AUTO_OFF; +} + +static void kvm_steal_time_set(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->kvm_steal_time = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; +} + +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) +{ + cpu->lbt = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "lbt", loongarch_get_lbt, + loongarch_set_lbt); + object_property_set_description(OBJECT(cpu), "lbt", + "Set off to disable Binary Tranlation."); + + cpu->pmu = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "pmu", loongarch_get_pmu, + loongarch_set_pmu); + object_property_set_description(OBJECT(cpu), "pmu", + "Set off to disable performance monitor unit."); + + cpu->kvm_pv_ipi = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "kvm-pv-ipi", kvm_pv_ipi_get, + kvm_pv_ipi_set); + object_property_set_description(OBJECT(cpu), "kvm-pv-ipi", + "Set off to disable KVM paravirt IPI."); + + cpu->kvm_steal_time = ON_OFF_AUTO_AUTO; + object_property_add_bool(OBJECT(cpu), "kvm-steal-time", kvm_steal_time_get, + kvm_steal_time_set); + object_property_set_description(OBJECT(cpu), "kvm-steal-time", + "Set off to disable KVM steal time."); +} + int kvm_arch_destroy_vcpu(CPUState *cs) { return 0; diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index 3fde5a5a20..6f732d80f3 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -40,7 +40,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) } static const char *cpu_model_advertised_features[] = { - "lsx", "lasx", "lbt", "pmu", NULL + "lsx", "lasx", "lbt", "pmu", "kvm-pv-ipi", "kvm-steal-time", NULL }; CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 41dfdf5804..2617d8f6ed 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -107,6 +107,41 @@ static void m68k_cpu_reset_hold(Object *obj, ResetType type) set_float_2nan_prop_rule(float_2nan_prop_ab, &env->fp_status); /* Default NaN: sign bit clear, all frac bits set */ set_float_default_nan_pattern(0b01111111, &env->fp_status); + /* + * m68k-specific floatx80 behaviour: + * * default Infinity values have a zero Integer bit + * * input Infinities may have the Integer bit either 0 or 1 + * * pseudo-denormals supported for input and output + * * don't raise Invalid for pseudo-NaN/pseudo-Inf/Unnormal + * + * With m68k, the explicit integer bit can be zero in the case of: + * - zeros (exp == 0, mantissa == 0) + * - denormalized numbers (exp == 0, mantissa != 0) + * - unnormalized numbers (exp != 0, exp < 0x7FFF) + * - infinities (exp == 0x7FFF, mantissa == 0) + * - not-a-numbers (exp == 0x7FFF, mantissa != 0) + * + * For infinities and NaNs, the explicit integer bit can be either one or + * zero. + * + * The IEEE 754 standard does not define a zero integer bit. Such a number + * is an unnormalized number. Hardware does not directly support + * denormalized and unnormalized numbers, but implicitly supports them by + * trapping them as unimplemented data types, allowing efficient conversion + * in software. + * + * See "M68000 FAMILY PROGRAMMER’S REFERENCE MANUAL", + * "1.6 FLOATING-POINT DATA TYPES" + * + * Note though that QEMU's fp emulation does directly handle both + * denormal and unnormal values, and does not trap to guest software. + */ + set_floatx80_behaviour(floatx80_default_inf_int_bit_is_zero | + floatx80_pseudo_inf_valid | + floatx80_pseudo_nan_valid | + floatx80_unnormal_valid | + floatx80_pseudo_denormal_valid, + &env->fp_status); nan = floatx80_default_nan(&env->fp_status); for (i = 0; i < 8; i++) { diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index 339b73ad7d..eb1cb8c687 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -455,7 +455,7 @@ void HELPER(ftst)(CPUM68KState *env, FPReg *val) if (floatx80_is_any_nan(val->d)) { cc |= FPSR_CC_A; - } else if (floatx80_is_infinity(val->d)) { + } else if (floatx80_is_infinity(val->d, &env->fp_status)) { cc |= FPSR_CC_I; } else if (floatx80_is_zero(val->d)) { cc |= FPSR_CC_Z; diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index 02dcc03d15..d1f150e641 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -142,8 +142,7 @@ floatx80 floatx80_scale(floatx80 a, floatx80 b, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaN(a, b, status); } - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } if (aExp == 0) { if (aSig == 0) { @@ -245,7 +244,7 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) float_raise(float_flag_invalid, status); return floatx80_default_nan(status); } - return packFloatx80(0, floatx80_infinity.high, floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -255,8 +254,7 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) if (aSign && aExp >= one_exp) { if (aExp == one_exp && aSig == one_sig) { float_raise(float_flag_divbyzero, status); - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } float_raise(float_flag_invalid, status); return floatx80_default_nan(status); @@ -442,8 +440,7 @@ floatx80 floatx80_logn(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } @@ -452,8 +449,7 @@ floatx80 floatx80_logn(floatx80 a, float_status *status) if (aExp == 0) { if (aSig == 0) { /* zero */ float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } if ((aSig & one_sig) == 0) { /* denormal */ normalizeFloatx80Subnormal(aSig, &aExp, &aSig); @@ -610,15 +606,13 @@ floatx80 floatx80_log10(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } if (aExp == 0 && aSig == 0) { float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } if (aSign) { @@ -668,16 +662,14 @@ floatx80 floatx80_log2(floatx80 a, float_status *status) propagateFloatx80NaNOneArg(a, status); } if (aSign == 0) { - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } } if (aExp == 0) { if (aSig == 0) { float_raise(float_flag_divbyzero, status); - return packFloatx80(1, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(1, status); } normalizeFloatx80Subnormal(aSig, &aExp, &aSig); } @@ -740,8 +732,7 @@ floatx80 floatx80_etox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -924,8 +915,7 @@ floatx80 floatx80_twotox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -1075,8 +1065,7 @@ floatx80 floatx80_tentox(floatx80 a, float_status *status) if (aSign) { return packFloatx80(0, 0, 0); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -2260,8 +2249,7 @@ floatx80 floatx80_atanh(floatx80 a, float_status *status) if (compact >= 0x3FFF8000) { /* |X| >= 1 */ if (aExp == one_exp && aSig == one_sig) { /* |X| == 1 */ float_raise(float_flag_divbyzero, status); - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } else { /* |X| > 1 */ float_raise(float_flag_invalid, status); return floatx80_default_nan(status); @@ -2320,8 +2308,7 @@ floatx80 floatx80_etoxm1(floatx80 a, float_status *status) if (aSign) { return packFloatx80(aSign, one_exp, one_sig); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { @@ -2687,8 +2674,7 @@ floatx80 floatx80_sinh(floatx80 a, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a, status); } - return packFloatx80(aSign, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(aSign, status); } if (aExp == 0 && aSig == 0) { @@ -2774,8 +2760,7 @@ floatx80 floatx80_cosh(floatx80 a, float_status *status) if ((uint64_t) (aSig << 1)) { return propagateFloatx80NaNOneArg(a, status); } - return packFloatx80(0, floatx80_infinity.high, - floatx80_infinity.low); + return floatx80_default_inf(0, status); } if (aExp == 0 && aSig == 0) { diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index cca24b9f1f..d7ecf729d0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -700,13 +700,6 @@ static void rv128_base_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); CPURISCVState *env = &cpu->env; - if (qemu_tcg_mttcg_enabled()) { - /* Missing 128-bit aligned atomics */ - error_report("128-bit RISC-V currently does not work with Multi " - "Threaded TCG. Please use: -accel tcg,thread=single"); - exit(EXIT_FAILURE); - } - cpu->cfg.mmu = true; cpu->cfg.pmp = true; @@ -3051,15 +3044,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) } #endif -#define DEFINE_CPU(type_name, misa_mxl_max, initfn) \ - { \ - .name = (type_name), \ - .parent = TYPE_RISCV_CPU, \ - .instance_init = (initfn), \ - .class_init = riscv_cpu_class_init, \ - .class_data = (void *)(misa_mxl_max) \ - } - #define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max, initfn) \ { \ .name = (type_name), \ diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 0a137281de..d7e694fdb3 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1014,6 +1014,7 @@ static bool riscv_cpu_is_generic(Object *cpu_obj) static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) { RISCVCPU *cpu = RISCV_CPU(cs); + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); if (!riscv_cpu_tcg_compatible(cpu)) { g_autofree char *name = riscv_cpu_get_name(cpu); @@ -1022,6 +1023,14 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) return false; } + if (mcc->misa_mxl_max >= MXL_RV128 && qemu_tcg_mttcg_enabled()) { + /* Missing 128-bit aligned atomics */ + error_setg(errp, + "128-bit RISC-V currently does not work with Multi " + "Threaded TCG. Please use: -accel tcg,thread=single"); + return false; + } + #ifndef CONFIG_USER_ONLY CPURISCVState *env = &cpu->env; diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 4ac693d99b..ccfe222bdf 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -128,6 +128,7 @@ static void superh_cpu_reset_hold(Object *obj, ResetType type) set_flush_to_zero(1, &env->fp_status); #endif set_default_nan_mode(1, &env->fp_status); + set_snan_bit_is_one(true, &env->fp_status); /* sign bit clear, set all frac bits other than msb */ set_float_default_nan_pattern(0b00111111, &env->fp_status); /* diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index b9b54a8793..3551532372 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -108,305 +108,3 @@ class ReplayKernelNormal(ReplayKernelBase): console_pattern = 'VFS: Cannot open root device' self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - # See https://gitlab.com/qemu-project/qemu/-/issues/2094 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'pc machine is unstable with replay') - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - :avocado: tags=flaky - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_x86_64_q35(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_arm_virt(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:virt - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/armhfp/os/images/pxeboot' - '/vmlinuz') - kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) - - def test_arm_cubieboard_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'panic=-1 noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, - args=('-dtb', dtb_path, - '-initrd', initrd_path, - '-no-reboot')) - - def test_s390x_s390_ccw_virtio(self): - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/s390x/os/images' - '/kernel.img') - kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) - - def test_alpha_clipper(self): - """ - :avocado: tags=arch:alpha - :avocado: tags=machine:clipper - """ - kernel_url = ('http://archive.debian.org/debian/dists/lenny/main/' - 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz') - kernel_hash = '3a943149335529e2ed3e74d0d787b85fb5671ba3' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - uncompressed_kernel = archive.uncompress(kernel_path, self.workdir) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9, - args=('-nodefaults', )) - - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_ppc64_powernv(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \ - 'console=tty0 console=hvc0' - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_m68k_q800(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:q800 - """ - deb_url = ('https://snapshot.debian.org/archive/debian-ports' - '/20191021T083923Z/pool-m68k/main' - '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb') - deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-5.3.0-1-m68k') - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 vga=off') - console_pattern = 'No filesystem could mount root' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def do_test_advcal_2018(self, file_path, kernel_name, args=None): - archive.extract(file_path, self.workdir) - - for entry in os.scandir(self.workdir): - if entry.name.startswith('day') and entry.is_dir(): - kernel_path = os.path.join(entry.path, kernel_name) - break - - kernel_command_line = '' - console_pattern = 'QEMU advent calendar' - self.run_rr(kernel_path, kernel_command_line, console_pattern, - args=args) - - def test_arm_vexpressa9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:vexpress-a9 - """ - tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day16.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb' - self.do_test_advcal_2018(file_path, 'winter.zImage', - args=('-dtb', dtb_path)) - - def test_m68k_mcf5208evb(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:mcf5208evb - """ - tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day07.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'sanity-clause.elf') - - def test_microblaze_s3adsp1800(self): - """ - :avocado: tags=arch:microblaze - :avocado: tags=machine:petalogix-s3adsp1800 - """ - tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day17.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'ballerina.bin') - - def test_ppc64_e500(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e5500 - """ - tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day19.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'uImage') - - def test_or1k_sim(self): - """ - :avocado: tags=arch:or1k - :avocado: tags=machine:or1k-sim - """ - tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day20.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'vmlinux') - - def test_ppc_g3beige(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:g3beige - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) - - def test_ppc_mac99(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mac99 - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) - - def test_sparc_ss20(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:SS-20 - """ - tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day11.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'zImage.elf') - - def test_xtensa_lx60(self): - """ - :avocado: tags=arch:xtensa - :avocado: tags=machine:lx60 - :avocado: tags=cpu:dc233c - """ - tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day02.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 111d8bab26..3fd2652c07 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -15,16 +15,16 @@ test_timeouts = { 'aarch64_raspi4' : 480, 'aarch64_rme_virt' : 1200, 'aarch64_rme_sbsaref' : 1200, - 'aarch64_sbsaref_alpine' : 720, + 'aarch64_sbsaref_alpine' : 1200, 'aarch64_sbsaref_freebsd' : 720, 'aarch64_tuxrun' : 240, 'aarch64_virt' : 720, 'acpi_bits' : 420, 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, - 'arm_aspeed_ast2500' : 480, - 'arm_aspeed_ast2600' : 720, - 'arm_aspeed_rainier' : 240, + 'arm_aspeed_ast2500' : 720, + 'arm_aspeed_ast2600' : 1200, + 'arm_aspeed_rainier' : 480, 'arm_bpim2u' : 500, 'arm_collie' : 180, 'arm_cubieboard' : 360, @@ -41,6 +41,7 @@ test_timeouts = { 'ppc64_hv' : 1000, 'ppc64_powernv' : 480, 'ppc64_pseries' : 480, + 'ppc64_replay' : 210, 'ppc64_tuxrun' : 420, 'ppc64_mac99' : 120, 'riscv64_tuxrun' : 120, @@ -48,6 +49,7 @@ test_timeouts = { 'sh4_tuxrun' : 240, 'virtio_balloon': 120, 'x86_64_kvm_xen' : 180, + 'x86_64_replay' : 480, } tests_generic_system = [ @@ -70,6 +72,7 @@ tests_aarch64_system_thorough = [ 'aarch64_aspeed', 'aarch64_raspi3', 'aarch64_raspi4', + 'aarch64_replay', 'aarch64_rme_virt', 'aarch64_rme_sbsaref', 'aarch64_sbsaref', @@ -89,6 +92,7 @@ tests_alpha_system_quick = [ tests_alpha_system_thorough = [ 'alpha_clipper', + 'alpha_replay', ] tests_arm_system_quick = [ @@ -112,6 +116,7 @@ tests_arm_system_thorough = [ 'arm_orangepi', 'arm_quanta_gsj', 'arm_raspi2', + 'arm_replay', 'arm_smdkc210', 'arm_sx1', 'arm_vexpress', @@ -146,11 +151,13 @@ tests_loongarch64_system_thorough = [ tests_m68k_system_thorough = [ 'm68k_mcf5208evb', 'm68k_nextcube', + 'm68k_replay', 'm68k_q800', 'm68k_tuxrun', ] tests_microblaze_system_thorough = [ + 'microblaze_replay', 'microblaze_s3adsp1800' ] @@ -183,6 +190,7 @@ tests_mips64el_system_thorough = [ ] tests_or1k_system_thorough = [ + 'or1k_replay', 'or1k_sim', ] @@ -198,6 +206,7 @@ tests_ppc_system_thorough = [ 'ppc_bamboo', 'ppc_mac', 'ppc_mpc8544ds', + 'ppc_replay', 'ppc_sam460ex', 'ppc_tuxrun', 'ppc_virtex_ml507', @@ -212,6 +221,7 @@ tests_ppc64_system_thorough = [ 'ppc64_hv', 'ppc64_powernv', 'ppc64_pseries', + 'ppc64_replay', 'ppc64_tuxrun', 'ppc64_mac99', ] @@ -240,6 +250,7 @@ tests_rx_system_thorough = [ tests_s390x_system_thorough = [ 's390x_ccw_virtio', + 's390x_replay', 's390x_topology', 's390x_tuxrun', ] @@ -258,6 +269,7 @@ tests_sparc_system_quick = [ ] tests_sparc_system_thorough = [ + 'sparc_replay', 'sparc_sun4m', ] @@ -291,11 +303,13 @@ tests_x86_64_system_thorough = [ 'x86_64_hotplug_blk', 'x86_64_hotplug_cpu', 'x86_64_kvm_xen', + 'x86_64_replay', 'x86_64_tuxrun', ] tests_xtensa_system_thorough = [ 'xtensa_lx60', + 'xtensa_replay', ] precache_all = [] diff --git a/tests/functional/replay_kernel.py b/tests/functional/replay_kernel.py index 8e8ac7d052..80795eb052 100644 --- a/tests/functional/replay_kernel.py +++ b/tests/functional/replay_kernel.py @@ -34,7 +34,7 @@ class ReplayKernelBase(LinuxKernelTest): logger = logging.getLogger('replay') start_time = time.time() - vm = self.get_vm() + vm = self.get_vm(name='recording' if record else 'replay') vm.set_console() if record: logger.info('recording the execution...') diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/test_aarch64_replay.py new file mode 100755 index 0000000000..04cde433bc --- /dev/null +++ b/tests/functional/test_aarch64_replay.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an aarch64 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Aarch64Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') + + def test_aarch64_virt(self): + self.set_machine('virt') + self.cpu = 'cortex-a53' + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_alpha_replay.py b/tests/functional/test_alpha_replay.py new file mode 100755 index 0000000000..24a17ef590 --- /dev/null +++ b/tests/functional/test_alpha_replay.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an Alpha machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class AlphaReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/dists/lenny/main/installer-alpha/' + '20090123lenny10/images/cdrom/vmlinuz'), + '34f53da3fa32212e4f00b03cb944b2ad81c06bc8faaf9b7193b2e544ceeca576') + + def test_clipper(self): + self.set_machine('clipper') + kernel_path = self.uncompress(self.ASSET_KERNEL, format='gz') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9, + args=('-nodefaults', )) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_arm_replay.py b/tests/functional/test_arm_replay.py new file mode 100755 index 0000000000..e002e6a264 --- /dev/null +++ b/tests/functional/test_arm_replay.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on arm machines and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class ArmReplay(ReplayKernelBase): + + ASSET_VIRT = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_virt(self): + self.set_machine('virt') + kernel_path = self.ASSET_VIRT.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) + + ASSET_CUBIE_KERNEL = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf_' + '_6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_CUBIE_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/arm/rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + def test_cubieboard(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb') + initrd_path = self.uncompress(self.ASSET_CUBIE_INITRD) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, + args=('-dtb', dtb_path, + '-initrd', initrd_path, + '-no-reboot')) + + ASSET_DAY16 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day16.tar.xz', + '63311adb2d4c4e7a73214a86d29988add87266a909719c56acfadd026b4110a7') + + def test_vexpressa9(self): + self.set_machine('vexpress-a9') + self.archive_extract(self.ASSET_DAY16) + kernel_path = self.scratch_file('day16', 'winter.zImage') + dtb_path = self.scratch_file('day16', 'vexpress-v2p-ca9.dtb') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-dtb', dtb_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py index b85bfaa178..4dd1e1859f 100755 --- a/tests/functional/test_arm_sx1.py +++ b/tests/functional/test_arm_sx1.py @@ -44,7 +44,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-no-reboot') self.launch_kernel(zimage_path, initrd=initrd_path) - self.vm.wait(timeout=60) + self.vm.wait(timeout=120) def test_arm_sx1_sd(self): self.set_machine('sx1') @@ -55,7 +55,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=sd,file={sd_fs_path}') self.launch_kernel(zimage_path) - self.vm.wait(timeout=60) + self.vm.wait(timeout=120) def test_arm_sx1_flash(self): self.set_machine('sx1') @@ -66,7 +66,7 @@ class SX1Test(LinuxKernelTest): self.vm.add_args('-snapshot') self.vm.add_args('-drive', f'format=raw,if=pflash,file={flash_path}') self.launch_kernel(zimage_path) - self.vm.wait(timeout=60) + self.vm.wait(timeout=120) if __name__ == '__main__': LinuxKernelTest.main() diff --git a/tests/functional/test_m68k_replay.py b/tests/functional/test_m68k_replay.py new file mode 100755 index 0000000000..18c1db539c --- /dev/null +++ b/tests/functional/test_m68k_replay.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an m68k machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class M68kReplay(ReplayKernelBase): + + ASSET_Q800 = Asset( + ('https://snapshot.debian.org/' + 'archive/debian-ports/20191021T083923Z/pool-m68k/main/l/linux/' + 'kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb'), + '949e50d74d4b9bc15d26c06d402717b7a4c0e32ff8100014f5930d8024de7b73') + + def test_q800(self): + self.set_machine('q800') + kernel_path = self.archive_extract(self.ASSET_Q800, + member='boot/vmlinux-5.3.0-1-m68k') + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 vga=off') + console_pattern = 'No filesystem could mount root' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + ASSET_MCF5208 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day07.tar.xz', + '753c2f3837126b7c6ba92d0b1e0b156e8a2c5131d2d576bb0b9a763fae73c08a') + + def test_mcf5208evb(self): + self.set_machine('mcf5208evb') + kernel_path = self.archive_extract(self.ASSET_MCF5208, + member='day07/sanity-clause.elf') + self.run_rr(kernel_path, self.KERNEL_COMMON_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_microblaze_replay.py b/tests/functional/test_microblaze_replay.py new file mode 100755 index 0000000000..7484c4186f --- /dev/null +++ b/tests/functional/test_microblaze_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an microblaze machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class MicroblazeReplay(ReplayKernelBase): + + ASSET_DAY17 = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day17.tar.xz'), + '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + + def test_microblaze_s3adsp1800(self): + self.set_machine('petalogix-s3adsp1800') + kernel_path = self.archive_extract(self.ASSET_DAY17, + member='day17/ballerina.bin') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index c4226f49cf..c93fa14232 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -7,6 +7,7 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +from qemu_test import exec_command_and_wait_for_pattern from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern @@ -20,6 +21,10 @@ class MicroblazeMachine(QemuSystemTest): 'day17.tar.xz'), '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + ASSET_IMAGE_LE = Asset( + ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), + 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') + def do_ballerina_be_test(self, machine): self.set_machine(machine) self.archive_extract(self.ASSET_IMAGE_BE) @@ -34,8 +39,30 @@ class MicroblazeMachine(QemuSystemTest): # message, that's why we don't test for a later string here. This # needs some investigation by a microblaze wizard one day... + def do_xmaton_le_test(self, machine): + self.require_netdev('user') + self.set_machine(machine) + self.archive_extract(self.ASSET_IMAGE_LE) + self.vm.set_console() + self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) + tftproot = self.scratch_file('day13') + self.vm.add_args('-nic', f'user,tftp={tftproot}') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') + wait_for_console_pattern(self, 'buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, + 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', + '821cd3cab8efd16ad6ee5acc3642a8ea') + + +class MicroblazeBigEndianMachine(MicroblazeMachine): + + ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE + def test_microblaze_s3adsp1800_legacy_be(self): self.do_ballerina_be_test('petalogix-s3adsp1800') + if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index 60aab4a45e..ab59941d57 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -7,37 +7,16 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from qemu_test import exec_command_and_wait_for_pattern -from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern +from test_microblaze_s3adsp1800 import MicroblazeMachine -class MicroblazeelMachine(QemuSystemTest): +class MicroblazeLittleEndianMachine(MicroblazeMachine): - timeout = 90 - - ASSET_IMAGE_LE = Asset( - ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), - 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') - - def do_xmaton_le_test(self, machine): - self.require_netdev('user') - self.set_machine(machine) - self.archive_extract(self.ASSET_IMAGE_LE) - self.vm.set_console() - self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) - tftproot = self.scratch_file('day13') - self.vm.add_args('-nic', f'user,tftp={tftproot}') - self.vm.launch() - wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') - wait_for_console_pattern(self, 'buildroot login:') - exec_command_and_wait_for_pattern(self, 'root', '#') - exec_command_and_wait_for_pattern(self, - 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', - '821cd3cab8efd16ad6ee5acc3642a8ea') + ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE def test_microblaze_s3adsp1800_legacy_le(self): self.do_xmaton_le_test('petalogix-s3adsp1800') + if __name__ == '__main__': - QemuSystemTest.main() + MicroblazeMachine.main() diff --git a/tests/functional/test_or1k_replay.py b/tests/functional/test_or1k_replay.py new file mode 100755 index 0000000000..2b60a9372c --- /dev/null +++ b/tests/functional/test_or1k_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an OpenRISC-1000 SIM machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Or1kReplay(ReplayKernelBase): + + ASSET_DAY20 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day20.tar.xz', + 'ff9d7dd7c6bdba325bd85ee85c02db61ff653e129558aeffe6aff55bffb6763a') + + def test_sim(self): + self.set_machine('or1k-sim') + kernel_path = self.archive_extract(self.ASSET_DAY20, + member='day20/vmlinux') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/test_ppc64_e500.py index b92fe0b0e7..9ce7ae6c47 100755 --- a/tests/functional/test_ppc64_e500.py +++ b/tests/functional/test_ppc64_e500.py @@ -5,20 +5,39 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern class E500Test(LinuxKernelTest): - ASSET_DAY19 = Asset( - 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day19.tar.xz', - '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + ASSET_BR2_E5500_UIMAGE = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/uImage', + '2478187c455d6cca3984e9dfde9c635d824ea16236b85fd6b4809f744706deda') - def test_ppc64_e500(self): + ASSET_BR2_E5500_ROOTFS = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/rootfs.ext2', + '9035ef97237c84c7522baaff17d25cdfca4bb7a053d5e296e902919473423d76') + + def test_ppc64_e500_buildroot(self): self.set_machine('ppce500') self.cpu = 'e5500' - self.archive_extract(self.ASSET_DAY19) - self.launch_kernel(self.scratch_file('day19', 'uImage'), - wait_for='QEMU advent calendar') + + uimage_path = self.ASSET_BR2_E5500_UIMAGE.fetch() + rootfs_path = self.ASSET_BR2_E5500_ROOTFS.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', uimage_path, + '-append', 'root=/dev/vda', + '-drive', f'file={rootfs_path},if=virtio,format=raw', + '-snapshot', '-no-shutdown') + self.vm.launch() + + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('lease of 10.0.2.15') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'Power down') if __name__ == '__main__': LinuxKernelTest.main() diff --git a/tests/functional/test_ppc64_replay.py b/tests/functional/test_ppc64_replay.py new file mode 100755 index 0000000000..48ce1b7f1e --- /dev/null +++ b/tests/functional/test_ppc64_replay.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on ppc64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Ppc64Replay(ReplayKernelBase): + + ASSET_DAY19 = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day19.tar.xz'), + '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + + def test_ppc64_e500(self): + self.set_machine('ppce500') + self.cpu = 'e5500' + kernel_path = self.archive_extract(self.ASSET_DAY19, + member='day19/uImage') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + + def test_ppc64_pseries(self): + self.set_machine('pseries') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + def test_ppc64_powernv(self): + self.set_machine('powernv') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \ + 'console=tty0 console=hvc0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_ppc_replay.py b/tests/functional/test_ppc_replay.py new file mode 100755 index 0000000000..8382070abd --- /dev/null +++ b/tests/functional/test_ppc_replay.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Replay tests for ppc machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class PpcReplay(ReplayKernelBase): + + ASSET_DAY15 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day15.tar.xz', + '03e0757c131d2959decf293a3572d3b96c5a53587165bf05ce41b2818a2bccd5') + + def do_day15_test(self): + self.require_accelerator("tcg") + kernel_path = self.archive_extract(self.ASSET_DAY15, + member='day15/invaders.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-M', 'graphics=off')) + + def test_g3beige(self): + self.set_machine('g3beige') + self.do_day15_test() + + def test_mac99(self): + self.set_machine('mac99') + self.do_day15_test() + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_s390x_replay.py b/tests/functional/test_s390x_replay.py new file mode 100755 index 0000000000..33b5843ada --- /dev/null +++ b/tests/functional/test_s390x_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an s390x machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class S390xReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/s390x/os/images/kernel.img'), + 'dace03b8ae0c9f670ebb9b8d6ce5eb24b62987f346de8f1300a439bb00bb99e7') + + def test_s390_ccw_virtio(self): + self.set_machine('s390-ccw-virtio') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_sparc_replay.py b/tests/functional/test_sparc_replay.py new file mode 100755 index 0000000000..865d6486f9 --- /dev/null +++ b/tests/functional/test_sparc_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on a sparc sun4m machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class SparcReplay(ReplayKernelBase): + + ASSET_DAY11 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day11.tar.xz', + 'c776533ba756bf4dd3f1fc4c024fb50ef0d853e05c5f5ddf0900a32d1eaa49e0') + + def test_replay(self): + self.set_machine('SS-10') + kernel_path = self.archive_extract(self.ASSET_DAY11, + member="day11/zImage.elf") + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/test_x86_64_replay.py new file mode 100755 index 0000000000..180f23a60c --- /dev/null +++ b/tests/functional/test_x86_64_replay.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on x86_64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipFlakyTest +from replay_kernel import ReplayKernelBase + + +class X86Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/29/Everything/x86_64/os/images/pxeboot/vmlinuz'), + '8f237d84712b1b411baf3af2aeaaee10b9aae8e345ec265b87ab3a39639eb143') + + def do_test_x86(self, machine): + self.set_machine(machine) + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2094') + def test_pc(self): + self.do_test_x86('pc') + + def test_q35(self): + self.do_test_x86('q35') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_xtensa_replay.py b/tests/functional/test_xtensa_replay.py new file mode 100755 index 0000000000..eb00a3b004 --- /dev/null +++ b/tests/functional/test_xtensa_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an xtensa lx650 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class XTensaReplay(ReplayKernelBase): + + ASSET_DAY02 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day02.tar.xz', + '68ff07f9b3fd3df36d015eb46299ba44748e94bfbb2d5295fddc1a8d4a9fd324') + + def test_replay(self): + self.set_machine('lx60') + self.cpu = 'dc233c' + kernel_path = self.archive_extract(self.ASSET_DAY02, + member='day02/santas-sleigh-ride.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 1e30a5bfe8..27d70bc11c 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -88,6 +88,17 @@ static void test_machine(gconstpointer data) qts = qtest_initf("-machine %s", machine); + if (g_test_slow()) { + /* Make sure we can get the machine class properties: */ + g_autofree char *qom_machine = g_strdup_printf("%s-machine", machine); + + response = qtest_qmp(qts, "{ 'execute': 'qom-list-properties'," + " 'arguments': { 'typename': %s } }", + qom_machine); + g_assert(response); + qobject_unref(response); + } + test_properties(qts, "/machine", true); response = qtest_qmp(qts, "{ 'execute': 'quit' }"); |